@reticlehq/server 2.13.1 → 2.14.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 (449) hide show
  1. package/SKILL.md +8 -18
  2. package/bin/reticle.js +3 -0
  3. package/dist/bridge/auth-failure-reason.js +12 -1
  4. package/dist/bridge/bridge-security.d.ts +8 -0
  5. package/dist/bridge/bridge-security.js +6 -1
  6. package/dist/bridge/bridge.d.ts +3 -0
  7. package/dist/bridge/bridge.js +23 -1
  8. package/dist/bridge/bridge.test-harness.d.ts +10 -1
  9. package/dist/bridge/bridge.test-harness.js +7 -1
  10. package/dist/bridge/pairing-token.d.ts +17 -0
  11. package/dist/bridge/pairing-token.js +26 -0
  12. package/dist/bridge/token-auth.d.ts +7 -0
  13. package/dist/bridge/token-auth.js +7 -1
  14. package/dist/capsule/capsule-store.d.ts +4 -4
  15. package/dist/capsule/capsule.d.ts +1 -1
  16. package/dist/capsule/capsule.js +3 -3
  17. package/dist/capsule/causal-summary.d.ts +21 -1
  18. package/dist/capsule/causal-summary.js +8 -6
  19. package/dist/capsule/divergence.d.ts +1 -1
  20. package/dist/capsule/divergence.js +26 -10
  21. package/dist/chromium-launch-options.d.ts +2 -1
  22. package/dist/cli/adhoc-verdict.d.ts +58 -0
  23. package/dist/cli/adhoc-verdict.js +115 -0
  24. package/dist/cli/chromium-hint.d.ts +18 -4
  25. package/dist/cli/chromium-hint.js +54 -6
  26. package/dist/cli/cli-doctor.js +1 -2
  27. package/dist/cli/cli-flow-commands.d.ts +16 -2
  28. package/dist/cli/cli-flow-commands.js +28 -9
  29. package/dist/cli/cli-parse-feedback.js +1 -1
  30. package/dist/cli/cli-parse.d.ts +2 -1
  31. package/dist/cli/cli-parse.js +38 -0
  32. package/dist/cli/cli-port.d.ts +5 -13
  33. package/dist/cli/cli-port.js +5 -38
  34. package/dist/cli/cli-update-commands.js +4 -4
  35. package/dist/cli/cli-verify.d.ts +24 -7
  36. package/dist/cli/cli-verify.js +87 -13
  37. package/dist/cli/cloud-keystore.d.ts +2 -1
  38. package/dist/cli/cloud-login.d.ts +2 -1
  39. package/dist/cli/cloud-session.d.ts +2 -1
  40. package/dist/cli/cloud-session.js +1 -1
  41. package/dist/cli/daemon-start-options.d.ts +2 -1
  42. package/dist/cli/device-flow.d.ts +2 -2
  43. package/dist/cli/doctor-daemon-line.d.ts +2 -1
  44. package/dist/cli/doctor-project-line.d.ts +2 -1
  45. package/dist/cli/doctor-rows.d.ts +2 -1
  46. package/dist/cli/drive-attach.d.ts +4 -3
  47. package/dist/cli/gate-hook-message.d.ts +2 -1
  48. package/dist/cli/status-next-action.js +10 -11
  49. package/dist/cli.d.ts +1 -0
  50. package/dist/cli.js +21 -9
  51. package/dist/cloud/account-state.d.ts +20 -0
  52. package/dist/cloud/account-state.js +65 -0
  53. package/dist/cloud/cloud-sync.d.ts +46 -3
  54. package/dist/cloud/cloud-sync.js +116 -0
  55. package/dist/cloud/sync-cycle.d.ts +5 -4
  56. package/dist/cloud/sync-cycle.js +1 -1
  57. package/dist/cloud/sync-daemon.d.ts +3 -19
  58. package/dist/cloud/sync-daemon.js +2 -2
  59. package/dist/crawl/crawl-tools.js +1 -1
  60. package/dist/crawl/crawl.d.ts +5 -5
  61. package/dist/crawl/crawl.js +138 -4
  62. package/dist/daemon/daemon-resilience.d.ts +3 -2
  63. package/dist/daemon/daemon-resilience.js +43 -4
  64. package/dist/daemon/daemon-resolve.d.ts +0 -9
  65. package/dist/daemon/daemon-resolve.js +2 -2
  66. package/dist/daemon/dev-servers.d.ts +1 -1
  67. package/dist/daemon/dev-servers.js +1 -1
  68. package/dist/daemon/loopback-alias.d.ts +2 -1
  69. package/dist/daemon/startup-failure.d.ts +11 -0
  70. package/dist/daemon/startup-failure.js +46 -0
  71. package/dist/events/contradictions.d.ts +10 -16
  72. package/dist/events/contradictions.js +149 -17
  73. package/dist/events/declared.d.ts +13 -1
  74. package/dist/events/declared.js +59 -3
  75. package/dist/events/echo-mismatch.js +75 -6
  76. package/dist/events/lineage.d.ts +52 -0
  77. package/dist/events/lineage.js +122 -0
  78. package/dist/events/net-evidence.d.ts +21 -0
  79. package/dist/events/net-evidence.js +81 -0
  80. package/dist/events/predicate-console.d.ts +13 -0
  81. package/dist/events/predicate-console.js +138 -0
  82. package/dist/events/predicate-element.d.ts +13 -0
  83. package/dist/events/predicate-element.js +214 -0
  84. package/dist/events/predicate-eval.d.ts +15 -3
  85. package/dist/events/predicate-eval.js +128 -110
  86. package/dist/events/predicate-parse.js +34 -1
  87. package/dist/events/predicate-request-body.d.ts +50 -0
  88. package/dist/events/predicate-request-body.js +120 -0
  89. package/dist/events/predicate-route.d.ts +21 -1
  90. package/dist/events/predicate-route.js +149 -21
  91. package/dist/events/predicate-schema.d.ts +8 -1
  92. package/dist/events/predicate-schema.js +34 -0
  93. package/dist/events/predicate.d.ts +2 -0
  94. package/dist/events/predicate.js +52 -159
  95. package/dist/events/split-text-miss.d.ts +6 -4
  96. package/dist/events/split-text-miss.js +12 -5
  97. package/dist/flows/affected-tools.js +22 -9
  98. package/dist/flows/annotate.d.ts +2 -1
  99. package/dist/flows/annotate.js +19 -1
  100. package/dist/flows/coverage.d.ts +2 -2
  101. package/dist/flows/flake.d.ts +3 -3
  102. package/dist/flows/flow-expect-grammar.d.ts +33 -0
  103. package/dist/flows/flow-expect-grammar.js +155 -0
  104. package/dist/flows/flow-replay-run.d.ts +1 -1
  105. package/dist/flows/flow-replay-run.js +28 -15
  106. package/dist/flows/flow-replay.d.ts +10 -0
  107. package/dist/flows/flow-replay.js +57 -22
  108. package/dist/flows/flow-secret-field.d.ts +14 -0
  109. package/dist/flows/flow-secret-field.js +22 -0
  110. package/dist/flows/flow-step-runners.d.ts +1 -0
  111. package/dist/flows/flow-step-runners.js +91 -34
  112. package/dist/flows/flow-success.js +22 -0
  113. package/dist/flows/flow-tools.js +27 -8
  114. package/dist/flows/flow-upload-resolve.d.ts +23 -0
  115. package/dist/flows/flow-upload-resolve.js +46 -0
  116. package/dist/flows/flows.d.ts +15 -0
  117. package/dist/flows/flows.js +17 -14
  118. package/dist/flows/git-changed.d.ts +23 -4
  119. package/dist/flows/git-changed.js +32 -6
  120. package/dist/flows/heal-run.js +2 -2
  121. package/dist/flows/predicate-to-expect.d.ts +3 -2
  122. package/dist/flows/predicate-to-expect.js +11 -5
  123. package/dist/flows/recording-backtrack.d.ts +24 -0
  124. package/dist/flows/recording-backtrack.js +73 -0
  125. package/dist/flows/recordings.d.ts +6 -0
  126. package/dist/flows/replay-halt.d.ts +26 -0
  127. package/dist/flows/replay-halt.js +30 -0
  128. package/dist/flows/replay.d.ts +0 -8
  129. package/dist/flows/replay.js +15 -3
  130. package/dist/flows/suite-flakes.d.ts +2 -1
  131. package/dist/flows/verify-change-tools.js +16 -4
  132. package/dist/honesty/already-true.d.ts +14 -0
  133. package/dist/honesty/already-true.js +47 -4
  134. package/dist/honesty/blind-spots.d.ts +3 -1
  135. package/dist/honesty/blind-spots.js +24 -1
  136. package/dist/honesty/body-capture-remedy.d.ts +42 -0
  137. package/dist/honesty/body-capture-remedy.js +95 -0
  138. package/dist/honesty/feature-capture.d.ts +3 -2
  139. package/dist/honesty/feature-capture.js +1 -1
  140. package/dist/honesty/gap-ledger.d.ts +1 -1
  141. package/dist/honesty/gap-ledger.js +1 -1
  142. package/dist/honesty/instrumentation-gaps.d.ts +8 -1
  143. package/dist/honesty/instrumentation-gaps.js +12 -2
  144. package/dist/honesty/observability.d.ts +4 -3
  145. package/dist/honesty/tool-hit-rate.d.ts +3 -2
  146. package/dist/honesty/uncaptured-bodies.d.ts +3 -1
  147. package/dist/honesty/uncaptured-bodies.js +6 -2
  148. package/dist/honesty/unsettled.d.ts +9 -0
  149. package/dist/honesty/unsettled.js +45 -0
  150. package/dist/honesty/verified.d.ts +32 -1
  151. package/dist/honesty/verified.js +37 -10
  152. package/dist/impact/impact-store.d.ts +5 -14
  153. package/dist/impact/impact-store.js +49 -6
  154. package/dist/index.js +1 -1
  155. package/dist/input/network-detail.d.ts +26 -1
  156. package/dist/input/network-detail.js +44 -12
  157. package/dist/input/network-mock-tools.js +24 -17
  158. package/dist/input/real-input.js +1 -1
  159. package/dist/input/viewport-tools.js +25 -13
  160. package/dist/intent/inline-intent.d.ts +0 -10
  161. package/dist/intent/inline-intent.js +1 -1
  162. package/dist/intent/intent-shard-store.d.ts +3 -2
  163. package/dist/intent/intent-shard-store.js +1 -1
  164. package/dist/intent/intent-shard.d.ts +14 -14
  165. package/dist/intent/intent-shard.js +4 -4
  166. package/dist/intent/intent-store.d.ts +1 -1
  167. package/dist/intent/intent-store.js +1 -1
  168. package/dist/intent/intent-subject.d.ts +2 -1
  169. package/dist/intent/open-intents.d.ts +3 -2
  170. package/dist/license/license.d.ts +4 -4
  171. package/dist/license/license.js +1 -1
  172. package/dist/mcp/attach-memory.d.ts +2 -1
  173. package/dist/mcp/client-identity.d.ts +2 -1
  174. package/dist/mcp/mcp-outage.d.ts +1 -1
  175. package/dist/mcp/mcp-outage.js +1 -1
  176. package/dist/mcp/mcp-proxy.d.ts +3 -1
  177. package/dist/mcp/mcp-proxy.js +32 -7
  178. package/dist/mcp/mcp.js +3 -1
  179. package/dist/mcp/proxy-handshake.d.ts +2 -1
  180. package/dist/mcp/proxy-idle-exit.d.ts +45 -0
  181. package/dist/mcp/proxy-idle-exit.js +94 -0
  182. package/dist/mcp/server-instructions.d.ts +2 -1
  183. package/dist/memory/project-memory.d.ts +2 -3
  184. package/dist/memory/project-memory.js +1 -1
  185. package/dist/oracles/propose-consequences.js +5 -5
  186. package/dist/oracles/self-instrument.d.ts +2 -2
  187. package/dist/oracles/self-instrument.js +1 -1
  188. package/dist/platform.d.ts +3 -5
  189. package/dist/platform.js +3 -5
  190. package/dist/pool/browser-pool.d.ts +92 -0
  191. package/dist/pool/browser-pool.js +119 -5
  192. package/dist/pool/playwright-launcher.js +29 -2
  193. package/dist/pool/storage-seed.d.ts +23 -0
  194. package/dist/pool/storage-seed.js +198 -0
  195. package/dist/project/artifact-root.d.ts +2 -1
  196. package/dist/project/memory-fs.d.ts +2 -1
  197. package/dist/project/reticle-dir.js +1 -1
  198. package/dist/project/session-root.d.ts +8 -0
  199. package/dist/project/session-root.js +9 -2
  200. package/dist/runs/reticle-runner.d.ts +14 -1
  201. package/dist/runs/reticle-runner.js +39 -2
  202. package/dist/session/attachment-history.d.ts +2 -1
  203. package/dist/session/fake-session.d.ts +37 -0
  204. package/dist/session/fake-session.js +81 -0
  205. package/dist/session/lease-visibility.d.ts +2 -1
  206. package/dist/session/live-control-tools.js +6 -0
  207. package/dist/session/no-session-diagnosis.d.ts +17 -1
  208. package/dist/session/no-session-diagnosis.js +32 -6
  209. package/dist/session/no-session-next-action.d.ts +14 -0
  210. package/dist/session/no-session-next-action.js +18 -0
  211. package/dist/session/no-session-watch.js +46 -3
  212. package/dist/session/registered-projects.d.ts +54 -0
  213. package/dist/session/registered-projects.js +73 -0
  214. package/dist/session/review-store.d.ts +2 -1
  215. package/dist/session/session-health.d.ts +62 -1
  216. package/dist/session/session-health.js +82 -2
  217. package/dist/session/session-manager.d.ts +11 -1
  218. package/dist/session/session-manager.js +18 -3
  219. package/dist/session/session-replaced.d.ts +24 -0
  220. package/dist/session/session-replaced.js +29 -0
  221. package/dist/session/session-successor.js +20 -4
  222. package/dist/session/session.d.ts +13 -5
  223. package/dist/session/session.js +31 -23
  224. package/dist/setup/agent-configs.d.ts +3 -2
  225. package/dist/setup/agent-writer.d.ts +3 -2
  226. package/dist/setup/approval-migration.d.ts +2 -1
  227. package/dist/setup/approval-migration.js +1 -1
  228. package/dist/setup/auto-approve.d.ts +11 -19
  229. package/dist/setup/auto-approve.js +15 -12
  230. package/dist/{init → setup}/confirm.js +1 -1
  231. package/dist/setup/desktop-shape.d.ts +3 -2
  232. package/dist/setup/drive-agent.d.ts +2 -3
  233. package/dist/setup/drive-agent.js +1 -1
  234. package/dist/setup/drive-plan.d.ts +3 -2
  235. package/dist/setup/ensure-daemon.d.ts +2 -1
  236. package/dist/setup/existing-app.d.ts +12 -0
  237. package/dist/setup/existing-app.js +13 -0
  238. package/dist/setup/init-host.d.ts +11 -0
  239. package/dist/setup/init-host.js +26 -0
  240. package/dist/setup/init-runtime.d.ts +4 -10
  241. package/dist/setup/init-runtime.js +23 -16
  242. package/dist/setup/license-key.d.ts +2 -1
  243. package/dist/setup/listeners.d.ts +2 -1
  244. package/dist/setup/loopback-probe-urls.d.ts +18 -0
  245. package/dist/setup/loopback-probe-urls.js +52 -0
  246. package/dist/setup/node-effects.d.ts +3 -0
  247. package/dist/setup/node-effects.js +26 -11
  248. package/dist/setup/page-probe.d.ts +5 -0
  249. package/dist/setup/relaunch.d.ts +2 -1
  250. package/dist/setup/run-setup.d.ts +5 -1
  251. package/dist/setup/run-setup.js +141 -50
  252. package/dist/setup/setup-command.d.ts +3 -2
  253. package/dist/setup/setup-command.js +8 -0
  254. package/dist/setup/setup-options.d.ts +5 -3
  255. package/dist/setup/setup-options.js +3 -2
  256. package/dist/setup/wait-progress.d.ts +28 -0
  257. package/dist/setup/wait-progress.js +43 -0
  258. package/dist/telemetry/app-instrumented.d.ts +2 -1
  259. package/dist/telemetry/app-instrumented.js +1 -1
  260. package/dist/telemetry/automation-hint.d.ts +1 -1
  261. package/dist/telemetry/automation-hint.js +1 -1
  262. package/dist/telemetry/cli-telemetry.js +1 -1
  263. package/dist/telemetry/connect-failure.d.ts +1 -1
  264. package/dist/telemetry/connect-failure.js +1 -1
  265. package/dist/telemetry/crash-cause.d.ts +3 -2
  266. package/dist/telemetry/crash-cause.js +1 -1
  267. package/dist/telemetry/daemon-telemetry.d.ts +3 -2
  268. package/dist/telemetry/daemon-telemetry.js +1 -1
  269. package/dist/telemetry/error-class.d.ts +3 -2
  270. package/dist/telemetry/error-class.js +1 -1
  271. package/dist/telemetry/feedback-cli.js +1 -1
  272. package/dist/telemetry/feedback-context.d.ts +3 -2
  273. package/dist/telemetry/feedback-context.js +5 -4
  274. package/dist/telemetry/feedback-local.d.ts +3 -2
  275. package/dist/telemetry/feedback.d.ts +4 -3
  276. package/dist/telemetry/feedback.js +1 -1
  277. package/dist/telemetry/git-facts.d.ts +2 -2
  278. package/dist/telemetry/git-facts.js +1 -1
  279. package/dist/telemetry/identify.d.ts +1 -1
  280. package/dist/telemetry/identify.js +1 -1
  281. package/dist/telemetry/init-telemetry.d.ts +2 -14
  282. package/dist/telemetry/init-telemetry.js +4 -13
  283. package/dist/telemetry/install-source.d.ts +7 -13
  284. package/dist/telemetry/install-source.js +7 -29
  285. package/dist/telemetry/license-activation.d.ts +3 -2
  286. package/dist/telemetry/license-activation.js +1 -1
  287. package/dist/telemetry/machine-snapshot.d.ts +1 -1
  288. package/dist/telemetry/mcp-connection.js +1 -1
  289. package/dist/telemetry/project-profile.d.ts +0 -14
  290. package/dist/telemetry/project-profile.js +1 -1
  291. package/dist/telemetry/proxy-telemetry.js +1 -1
  292. package/dist/telemetry/session-metrics.d.ts +1 -1
  293. package/dist/telemetry/session-metrics.js +1 -1
  294. package/dist/telemetry/telemetry.d.ts +1 -17
  295. package/dist/telemetry/telemetry.js +4 -4
  296. package/dist/telemetry/tool-refused.d.ts +1 -1
  297. package/dist/telemetry/tool-refused.js +1 -1
  298. package/dist/tools/act-observation.d.ts +3 -2
  299. package/dist/tools/act-preflight.js +8 -3
  300. package/dist/tools/act-target.d.ts +4 -0
  301. package/dist/tools/act-target.js +14 -5
  302. package/dist/tools/act-tools.d.ts +0 -2
  303. package/dist/tools/act-tools.js +45 -15
  304. package/dist/tools/assert-source.d.ts +30 -0
  305. package/dist/tools/assert-source.js +21 -0
  306. package/dist/tools/assert-verdict.d.ts +1 -1
  307. package/dist/tools/assert-verdict.js +14 -4
  308. package/dist/tools/browser-tools.js +28 -8
  309. package/dist/tools/complete-snapshot.d.ts +3 -2
  310. package/dist/tools/dynamic-tools.js +8 -0
  311. package/dist/tools/error-recovery.d.ts +10 -0
  312. package/dist/tools/error-recovery.js +23 -0
  313. package/dist/tools/feedback-tools.js +1 -1
  314. package/dist/tools/invoke-tool.js +2 -0
  315. package/dist/tools/lease-tools.d.ts +48 -20
  316. package/dist/tools/lease-tools.js +311 -76
  317. package/dist/tools/lineage-tools.d.ts +14 -0
  318. package/dist/tools/lineage-tools.js +64 -0
  319. package/dist/tools/merge-tools.js +1 -3
  320. package/dist/tools/navigate-arrival.d.ts +45 -1
  321. package/dist/tools/navigate-arrival.js +39 -6
  322. package/dist/tools/navigate-result.d.ts +8 -1
  323. package/dist/tools/navigate-result.js +14 -4
  324. package/dist/tools/numeric-bounds.d.ts +0 -11
  325. package/dist/tools/numeric-bounds.js +4 -4
  326. package/dist/tools/observe-tools.js +22 -26
  327. package/dist/tools/observed-nothing.d.ts +9 -0
  328. package/dist/tools/observed-nothing.js +2 -1
  329. package/dist/tools/read-tools.js +12 -17
  330. package/dist/tools/real-input-attempt.d.ts +14 -6
  331. package/dist/tools/real-input-attempt.js +75 -18
  332. package/dist/tools/reconcile-tools.js +2 -1
  333. package/dist/tools/reload-result.d.ts +6 -1
  334. package/dist/tools/reload-result.js +7 -2
  335. package/dist/tools/resolve-target.d.ts +7 -1
  336. package/dist/tools/resolve-target.js +72 -9
  337. package/dist/tools/settle-in-flight.d.ts +1 -1
  338. package/dist/tools/settle-in-flight.js +9 -2
  339. package/dist/tools/snapshot-delta.d.ts +24 -1
  340. package/dist/tools/snapshot-delta.js +66 -7
  341. package/dist/tools/tool-names.d.ts +1 -0
  342. package/dist/tools/tool-names.js +1 -0
  343. package/dist/tools/tool-surface.js +20 -2
  344. package/dist/tools/tools.js +120 -11
  345. package/dist/tools/verify-next-baton.d.ts +2 -1
  346. package/dist/update/sdk-sync.d.ts +1 -1
  347. package/dist/update/sdk-sync.js +1 -1
  348. package/dist/update/updater.js +1 -1
  349. package/dist/version/sdk-fix.d.ts +2 -1
  350. package/dist/version/version-skew.d.ts +0 -8
  351. package/dist/version/version-skew.js +1 -1
  352. package/docs/README.md +8 -6
  353. package/docs/actions.mdx +1 -1
  354. package/docs/agent-cheatsheet.md +4 -2
  355. package/docs/architecture.md +1 -1
  356. package/docs/cli/mcp.mdx +3 -1
  357. package/docs/cli/watch.mdx +1 -1
  358. package/docs/cli.mdx +1 -1
  359. package/docs/desktop.mdx +1 -1
  360. package/docs/docs.json +8 -3
  361. package/docs/faq.mdx +9 -0
  362. package/docs/first-drive.md +136 -0
  363. package/docs/frameworks.mdx +29 -7
  364. package/docs/gate-plan.md +2 -2
  365. package/docs/gates.md +19 -11
  366. package/docs/getting-started.md +7 -1
  367. package/docs/http-transport.md +164 -0
  368. package/docs/index.mdx +5 -3
  369. package/docs/install-manual.mdx +1 -1
  370. package/docs/packages/tauri.mdx +1 -1
  371. package/docs/packages.mdx +15 -3
  372. package/docs/platform-integration.md +1 -1
  373. package/docs/predicates.mdx +2 -0
  374. package/docs/system-map.md +2 -2
  375. package/docs/telemetry-contract.md +4 -4
  376. package/docs/telemetry-events.mdx +1 -1
  377. package/docs/telemetry.md +3 -3
  378. package/docs/tools-navigate.mdx +14 -6
  379. package/docs/usage.md +4 -4
  380. package/package.json +7 -4
  381. package/dist/init/agent-rules.d.ts +0 -116
  382. package/dist/init/agent-rules.js +0 -279
  383. package/dist/init/app-choice.d.ts +0 -20
  384. package/dist/init/app-choice.js +0 -25
  385. package/dist/init/astro-host.d.ts +0 -21
  386. package/dist/init/astro-host.js +0 -27
  387. package/dist/init/astro-patch.d.ts +0 -33
  388. package/dist/init/astro-patch.js +0 -300
  389. package/dist/init/capabilities.d.ts +0 -40
  390. package/dist/init/capabilities.js +0 -150
  391. package/dist/init/closing-hint.d.ts +0 -36
  392. package/dist/init/closing-hint.js +0 -120
  393. package/dist/init/connect-steps.d.ts +0 -2
  394. package/dist/init/connect-steps.js +0 -50
  395. package/dist/init/cra.d.ts +0 -79
  396. package/dist/init/cra.js +0 -178
  397. package/dist/init/csp-check.d.ts +0 -38
  398. package/dist/init/csp-check.js +0 -107
  399. package/dist/init/csp-doctor.d.ts +0 -32
  400. package/dist/init/csp-doctor.js +0 -54
  401. package/dist/init/desktop-doctor.d.ts +0 -41
  402. package/dist/init/desktop-doctor.js +0 -198
  403. package/dist/init/detect.d.ts +0 -96
  404. package/dist/init/detect.js +0 -209
  405. package/dist/init/dev-script.d.ts +0 -50
  406. package/dist/init/dev-script.js +0 -66
  407. package/dist/init/format-generated.d.ts +0 -18
  408. package/dist/init/format-generated.js +0 -39
  409. package/dist/init/install-hint.d.ts +0 -9
  410. package/dist/init/install-hint.js +0 -29
  411. package/dist/init/mcp-clients.d.ts +0 -89
  412. package/dist/init/mcp-clients.js +0 -299
  413. package/dist/init/mcp-registered.d.ts +0 -12
  414. package/dist/init/mcp-registered.js +0 -14
  415. package/dist/init/mcp.d.ts +0 -89
  416. package/dist/init/mcp.js +0 -129
  417. package/dist/init/next-patch.d.ts +0 -39
  418. package/dist/init/next-patch.js +0 -196
  419. package/dist/init/node-io.d.ts +0 -7
  420. package/dist/init/node-io.js +0 -117
  421. package/dist/init/non-js-project.d.ts +0 -27
  422. package/dist/init/non-js-project.js +0 -84
  423. package/dist/init/patch-kind.d.ts +0 -20
  424. package/dist/init/patch-kind.js +0 -10
  425. package/dist/init/plan-framework.d.ts +0 -64
  426. package/dist/init/plan-framework.js +0 -565
  427. package/dist/init/plan.d.ts +0 -242
  428. package/dist/init/plan.js +0 -703
  429. package/dist/init/preflight.d.ts +0 -28
  430. package/dist/init/preflight.js +0 -38
  431. package/dist/init/project-id.d.ts +0 -12
  432. package/dist/init/project-id.js +0 -25
  433. package/dist/init/refresh-rules.d.ts +0 -39
  434. package/dist/init/refresh-rules.js +0 -51
  435. package/dist/init/run.d.ts +0 -117
  436. package/dist/init/run.js +0 -806
  437. package/dist/init/slash-command.d.ts +0 -27
  438. package/dist/init/slash-command.js +0 -132
  439. package/dist/init/snippets.d.ts +0 -134
  440. package/dist/init/snippets.js +0 -572
  441. package/dist/init/vite-config.d.ts +0 -18
  442. package/dist/init/vite-config.js +0 -114
  443. package/dist/init/windows-quote.d.ts +0 -38
  444. package/dist/init/windows-quote.js +0 -70
  445. package/dist/init/workspace-apps.d.ts +0 -30
  446. package/dist/init/workspace-apps.js +0 -178
  447. package/dist/project/remember-project.d.ts +0 -24
  448. package/dist/project/remember-project.js +0 -40
  449. /package/dist/{init → setup}/confirm.d.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { dedupeGaps } from '@reticlehq/core';
1
+ import { dedupeGaps } from '@reticlehq/core/artifacts';
2
2
  /**
3
3
  * What this session still cannot see, accumulated across its verdicts.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { type InstrumentationGap } from '@reticlehq/core';
1
+ import { type InstrumentationGap } from '@reticlehq/core/artifacts';
2
2
  /**
3
3
  * The gaps ONE action revealed, derived from facts the act path already holds.
4
4
  *
@@ -75,6 +75,13 @@ export interface ActionInstrumentationFacts {
75
75
  * finished, and a bare flag makes them read the same.
76
76
  */
77
77
  openIntentCount?: number;
78
+ /**
79
+ * The page announced that this build has the `data-reticle-source` stamp switched OFF.
80
+ *
81
+ * Only `true` means anything: absent is "nobody said", which is every SDK predating the field and
82
+ * every hand-wired connect. See the HELLO field of the same name.
83
+ */
84
+ sourceMappingDisabled?: boolean;
78
85
  /**
79
86
  * How long the OLDEST open intent has been owed, in ms.
80
87
  *
@@ -1,10 +1,20 @@
1
- import { InstrumentationGapKind, dedupeGaps, instrumentationGap, } from '@reticlehq/core';
1
+ import { InstrumentationGapKind, dedupeGaps, instrumentationGap, } from '@reticlehq/core/artifacts';
2
2
  export function gapsForAction(facts) {
3
3
  const gaps = [];
4
4
  // A red verdict names the control and cannot name the line that renders it. That is the round trip
5
5
  // the agent is about to spend, and the one a build plugin removes permanently.
6
+ //
7
+ // ...unless the project TURNED THE STAMP OFF, which is a different situation with the opposite
8
+ // remedy. Reported from the field: a react-three-fiber app disabled `sourceMapping` because the
9
+ // stamp crashed it to a white screen, and every red verdict afterwards told the reader to install
10
+ // a build plugin that was already there. Only a definite `false` counts — an SDK that says nothing
11
+ // is unknown, and reading silence as an opt-out would mute the honest gap for every app that
12
+ // simply has no source mapping at all.
6
13
  if (!facts.pass && facts.source === undefined) {
7
- gaps.push(instrumentationGap(InstrumentationGapKind.NO_SOURCE_MAPPING, 'the control that was driven carries no source mapping', 'this verdict can name the control but not the file and line that render it, so finding the code is a separate search', { ...(facts.ref === undefined ? {} : { ref: facts.ref }) }));
14
+ const off = true === facts.sourceMappingDisabled;
15
+ gaps.push(instrumentationGap(off ? InstrumentationGapKind.SOURCE_MAPPING_OFF : InstrumentationGapKind.NO_SOURCE_MAPPING, off
16
+ ? 'source mapping is turned off for this build, so no element carries data-reticle-source'
17
+ : 'the control that was driven carries no source mapping', 'this verdict can name the control but not the file and line that render it, so finding the code is a separate search', { ...(facts.ref === undefined ? {} : { ref: facts.ref }) }));
8
18
  }
9
19
  // Asked about state, and there is no state channel to answer from. Deliberately UNLOCATED: a store
10
20
  // is registered once at app setup, nowhere near the control that was driven, and nothing in this
@@ -1,4 +1,4 @@
1
- import type { InstrumentationGap } from '@reticlehq/core';
1
+ import type { InstrumentationGap } from '@reticlehq/core/artifacts';
2
2
  /**
3
3
  * Of the controls this session drove, how many could Reticle fully observe?
4
4
  *
@@ -19,7 +19,7 @@ import type { InstrumentationGap } from '@reticlehq/core';
19
19
  * happened to be driven would put the number on the wrong thing and make it move for reasons the
20
20
  * agent cannot act on. Those gaps are reported alongside, not divided into.
21
21
  */
22
- export interface Observability {
22
+ interface Observability {
23
23
  /** Distinct controls driven this session. */
24
24
  driven: number;
25
25
  /** How many of them Reticle could fully observe. */
@@ -28,10 +28,11 @@ export interface Observability {
28
28
  percent?: number;
29
29
  }
30
30
  export declare function observabilityOf(drivenRefs: Iterable<string>, gaps: readonly InstrumentationGap[]): Observability;
31
- export interface CoverageRegression {
31
+ interface CoverageRegression {
32
32
  was: number;
33
33
  now: number;
34
34
  }
35
35
  export declare function coverageRegressed(best: {
36
36
  percent: number;
37
37
  } | undefined, current: Observability): CoverageRegression | undefined;
38
+ export {};
@@ -47,12 +47,12 @@ export type ToolTier = (typeof ToolTier)[keyof typeof ToolTier];
47
47
  /** Which surface would have put this tool in front of an agent, read off that surface's own set. */
48
48
  export declare function tierOfTool(tool: string): ToolTier;
49
49
  /** How many tools fell in each tier on one side of the called/never-called split. */
50
- export interface TierCounts {
50
+ interface TierCounts {
51
51
  default: number;
52
52
  extended: number;
53
53
  runOnly: number;
54
54
  }
55
- export interface ToolHitRate {
55
+ interface ToolHitRate {
56
56
  /** False when this session carries no ledger — "not watched", never "nothing was called". */
57
57
  observed: boolean;
58
58
  /** The four-plus-two cells: every tier, called and never-called. Omitted when not observed. */
@@ -84,3 +84,4 @@ export declare function foldToolHitRate(input: {
84
84
  calls: ReadonlyMap<string, number> | undefined;
85
85
  allTools: readonly string[];
86
86
  }): ToolHitRate;
87
+ export {};
@@ -16,6 +16,8 @@ export declare function bodiesNotCaptured(calls: {
16
16
  method?: string;
17
17
  requestBody?: string;
18
18
  responseBody?: string;
19
- }[]): {
19
+ }[],
20
+ /** The page SDK's version, so the remedy can check whether it applies. See body-capture-remedy. */
21
+ sdkVersion?: string): {
20
22
  bodiesNotCaptured?: string;
21
23
  };
@@ -1,3 +1,4 @@
1
+ import { bodyCaptureRemedy } from './body-capture-remedy.js';
1
2
  /** Methods that normally carry a payload — the ones where a missing body is worth explaining. */
2
3
  const BODY_BEARING_METHODS = new Set(['POST', 'PUT', 'PATCH']);
3
4
  /**
@@ -14,7 +15,9 @@ const BODY_BEARING_METHODS = new Set(['POST', 'PUT', 'PATCH']);
14
15
  * anywhere in the result, so an app that already captures bodies pays nothing and the field's
15
16
  * PRESENCE is the warning.
16
17
  */
17
- export function bodiesNotCaptured(calls) {
18
+ export function bodiesNotCaptured(calls,
19
+ /** The page SDK's version, so the remedy can check whether it applies. See body-capture-remedy. */
20
+ sdkVersion) {
18
21
  const bodyBearing = calls.filter((c) => BODY_BEARING_METHODS.has((c.method ?? '').toUpperCase()));
19
22
  if (0 === bodyBearing.length)
20
23
  return {};
@@ -32,6 +35,7 @@ export function bodiesNotCaptured(calls) {
32
35
  // config they do not have. Remediation that does not apply to the reader's stack is worse than
33
36
  // none: it sends them to edit a file that is not there and reads as a tool that does not know
34
37
  // what it is looking at.
35
- bodiesNotCaptured: 'request/response bodies are NOT being recorded, so an absent body here means UNSEEN, not empty — the payload of these calls was never inspected. Turn it on where your app calls connect(): `reticle.connect({ captureNetworkBodies: true })`, or for the Vite plugin `reticle({ captureNetworkBodies: true })` / VITE_RETICLE_CAPTURE_BODIES=1. Then re-run the action.',
38
+ bodiesNotCaptured: 'request/response bodies are NOT being recorded, so an absent body here means UNSEEN, not ' +
39
+ `empty — the payload of these calls was never inspected. ${bodyCaptureRemedy(sdkVersion)}`,
36
40
  };
37
41
  }
@@ -38,6 +38,15 @@ export interface UnsettledWindow {
38
38
  * agent that never chose that wait cannot be expected to recognise it in a timeout about it.
39
39
  */
40
40
  export declare function describeWaitTarget(predicate: Predicate): string;
41
+ /**
42
+ * Did the assertion name a request that is still open?
43
+ *
44
+ * `waitForPredicate` reports a miss ("no request to /register") the moment the budget ends, even
45
+ * when that same POST is sitting in `stillInFlight`. Grading that miss `no` is a race against the
46
+ * backend, not a defect. Matching is the discriminator: an unrelated poll left open does not pardon
47
+ * a named URL that never started.
48
+ */
49
+ export declare function namedNetIsInFlight(predicate: Predicate, stillInFlight: readonly string[]): boolean;
41
50
  /**
42
51
  * The `because` sentence for an unsettled verdict: the wait, the observation, the next move.
43
52
  *
@@ -52,6 +52,51 @@ export function describeWaitTarget(predicate) {
52
52
  return `the absence of ${describeWaitTarget(predicate.predicate)}`;
53
53
  }
54
54
  }
55
+ /**
56
+ * Did the assertion name a request that is still open?
57
+ *
58
+ * `waitForPredicate` reports a miss ("no request to /register") the moment the budget ends, even
59
+ * when that same POST is sitting in `stillInFlight`. Grading that miss `no` is a race against the
60
+ * backend, not a defect. Matching is the discriminator: an unrelated poll left open does not pardon
61
+ * a named URL that never started.
62
+ */
63
+ export function namedNetIsInFlight(predicate, stillInFlight) {
64
+ if (0 === stillInFlight.length)
65
+ return false;
66
+ const nets = [];
67
+ const walk = (p) => {
68
+ switch (p.kind) {
69
+ case PredicateKind.ALL_OF:
70
+ case PredicateKind.ANY_OF:
71
+ for (const child of p.predicates)
72
+ walk(child);
73
+ return;
74
+ case PredicateKind.NET: {
75
+ if (undefined === p.urlContains || 0 === p.urlContains.length)
76
+ return;
77
+ nets.push({
78
+ ...(undefined === p.method ? {} : { method: p.method }),
79
+ urlContains: p.urlContains,
80
+ });
81
+ return;
82
+ }
83
+ default:
84
+ return;
85
+ }
86
+ };
87
+ walk(predicate);
88
+ return nets.some((net) => stillInFlight.some((label) => inFlightLabelMatchesNet(label, net)));
89
+ }
90
+ function inFlightLabelMatchesNet(label, net) {
91
+ const space = label.indexOf(' ');
92
+ if (space <= 0)
93
+ return false;
94
+ const method = label.slice(0, space);
95
+ const url = label.slice(space + 1);
96
+ if (undefined !== net.method && method.toUpperCase() !== net.method.toUpperCase())
97
+ return false;
98
+ return url.includes(net.urlContains);
99
+ }
55
100
  /**
56
101
  * The `because` sentence for an unsettled verdict: the wait, the observation, the next move.
57
102
  *
@@ -24,6 +24,14 @@ interface VerifiedInputs {
24
24
  * confidently-wrong-by-accident this clause exists to remove.
25
25
  */
26
26
  observationLost?: boolean;
27
+ /**
28
+ * The URL the session was on when observation was lost.
29
+ *
30
+ * Only meaningful with `observationLost`. A route that 500s tears the page down mid-wait; naming
31
+ * the last URL in the verdict is the difference between "install problem" and "the page died on
32
+ * /X" (#808). Absent when we never knew, which must not invent a route.
33
+ */
34
+ lastUrl?: string;
27
35
  honesty: HonestyBlock;
28
36
  /**
29
37
  * Set when the assertion could not be EVALUATED at all — an under-specified call, or nothing
@@ -36,6 +44,15 @@ interface VerifiedInputs {
36
44
  * ones are floored at the act's cursor and cannot be satisfied by the past.
37
45
  */
38
46
  alreadyTrue?: boolean;
47
+ /**
48
+ * The `alreadyTrue` match was against a HIDDEN element/text — DOM presence, not something a person
49
+ * could see on screen. `text`/`element` predicates match presence by default (`visible: true` is
50
+ * opt-in), so a dialog's content mounted-but-hidden before the click reads identically to content
51
+ * that was genuinely already showing, and named neither fact (#889). Only changes `because`; the
52
+ * verdict itself is unchanged; a hidden pre-existing match is exactly as unfalsifiable as a visible
53
+ * one.
54
+ */
55
+ alreadyTrueHiddenMatch?: boolean;
39
56
  /** Cross-channel disagreements found in the action's window. */
40
57
  contradictions?: readonly {
41
58
  kind: string;
@@ -43,7 +60,7 @@ interface VerifiedInputs {
43
60
  /** Did a real frame flush before the wait gave up? */
44
61
  settled?: boolean;
45
62
  /**
46
- * The caller NAMED the expected consequence before acting — an `until`/`predicate` of its own,
63
+ * The caller named a consequence before acting — an `until`/`predicate` of its own,
47
64
  * rather than the default "wait for the page to go idle".
48
65
  *
49
66
  * This is the epistemic core of the tool: a declaration made before the action cannot be
@@ -61,6 +78,12 @@ interface VerifiedInputs {
61
78
  * outright. Fixing a false negative must not open a path to a false positive.
62
79
  */
63
80
  declaredConsequence?: boolean;
81
+ /**
82
+ * A net the caller named is still open. `waitForPredicate` reports that as `pass: false` ("no
83
+ * request to …") the instant the budget ends, which made a cold backend `assertion_failed` and a
84
+ * warm one `proved`. Absence of a settle is not evidence the request never happened — see #669.
85
+ */
86
+ namedRequestInFlight?: boolean;
64
87
  /**
65
88
  * A write in this window answered `202 Accepted` — the server took the request and has NOT
66
89
  * finished processing it.
@@ -76,6 +99,14 @@ interface VerifiedInputs {
76
99
  * Empty means nothing went unread.
77
100
  */
78
101
  outcomeUnread?: readonly string[];
102
+ /**
103
+ * The page SDK's version, so the unread-body remedy can check whether it applies.
104
+ *
105
+ * This clause is the highest-traffic producer of that advice — every 2xx write whose body went
106
+ * unread lands here — and it inlined the setting unconditionally, which is what a reporter saw six
107
+ * times at a repo pinned below the release that introduced it. See body-capture-remedy.ts.
108
+ */
109
+ sdkVersion?: string | undefined;
79
110
  /**
80
111
  * The declared consequence that held does not depend on the response body — an exact string
81
112
  * rendered, a store path, a signal, a route, or an element located by role / name / testid. See
@@ -1,6 +1,8 @@
1
- import { ContradictionKind, MUTATING_METHODS, Verified, VerifiedReason, isAbsenceDerived, } from '@reticlehq/core';
1
+ import { bodyCaptureRemedy } from './body-capture-remedy.js';
2
+ import { ContradictionKind, MUTATING_METHODS, Verified, VerifiedReason, isAbsenceDerived, isAdvisory, } from '@reticlehq/core';
2
3
  import { HonestyGrade } from './honesty.js';
3
4
  import { unsettledBecause } from './unsettled.js';
5
+ import { pageTornDownWhileOn } from '../session/no-session-diagnosis.js';
4
6
  export function decideVerified(inputs) {
5
7
  const { pass, honesty, contradictions = [], settled, outcomePending, outcomeUnread } = inputs;
6
8
  /** The caller named a consequence before acting and it held — see `declaredConsequence`. */
@@ -22,10 +24,13 @@ export function decideVerified(inputs) {
22
24
  //
23
25
  // UNKNOWN, for the same reason `unclean_capture` is: the evidence is ABSENT, not negative.
24
26
  if (true === inputs.observationLost) {
27
+ const lost = undefined === inputs.lastUrl || '' === inputs.lastUrl
28
+ ? 'the tab disconnected'
29
+ : pageTornDownWhileOn(inputs.lastUrl);
25
30
  return {
26
31
  verified: Verified.UNKNOWN,
27
32
  verifiedReason: VerifiedReason.OBSERVATION_LOST,
28
- because: 'the tab disconnected while this action was being observed, so its outcome was never ' +
33
+ because: `${lost} while this action was being observed, so its outcome was never ` +
29
34
  'seen — this says nothing about the app. Call reticle_sessions for the current session ' +
30
35
  '(a reloaded tab keeps its id; a closed one is gone) and repeat the action if it is safe ' +
31
36
  'to repeat',
@@ -49,6 +54,20 @@ export function decideVerified(inputs) {
49
54
  '`until` naming what the action should cause (a signal, a request, a route, or store state)',
50
55
  };
51
56
  }
57
+ // ABOVE the failure clause, same reason as observationLost: absence is not evidence.
58
+ //
59
+ // A `{ net, urlContains }` miss while that URL is still in `stillInFlight` is a window that
60
+ // closed early, not a request that never happened. The same result object already named the
61
+ // in-flight POST; calling it `assertion_failed` contradicted that field and flipped green on a
62
+ // warm backend. UNKNOWN, and the sentence says to re-check once the request lands.
63
+ if (false === pass && true === inputs.namedRequestInFlight) {
64
+ return {
65
+ verified: Verified.UNKNOWN,
66
+ verifiedReason: VerifiedReason.WINDOW_CLOSED_EARLY,
67
+ because: unsettledBecause('the request this assertion named was still in flight when the window closed, so the ' +
68
+ 'consequence was not disproved — it had not finished', inputs.unsettled),
69
+ };
70
+ }
52
71
  // A failed assertion is the most actionable fact there is; it leads — including over
53
72
  // `alreadyTrue`, because a condition that held before AND fails now is a real regression.
54
73
  if (false === pass) {
@@ -72,7 +91,11 @@ export function decideVerified(inputs) {
72
91
  // every `no` verdict in the field. See ABSENCE_DERIVED_CONTRADICTIONS for why a false negative
73
92
  // costs more than it looks: it makes an agent redo work that succeeded, or stop believing the
74
93
  // verdict channel, which is the product.
75
- const observed = contradictions.filter((c) => !isAbsenceDerived(c.kind));
94
+ // Advisory findings are dropped from the decision entirely, at both tiers below. They are true and
95
+ // they ride out in `contradictions`; what they are not is evidence about the consequence the
96
+ // caller declared, because they concern traffic the assertion never named (#673).
97
+ const deciding = contradictions.filter((c) => !isAdvisory(c.kind));
98
+ const observed = deciding.filter((c) => !isAbsenceDerived(c.kind));
76
99
  if (observed.length > 0) {
77
100
  const kinds = observed.map((c) => c.kind).join(', ');
78
101
  return {
@@ -99,7 +122,7 @@ export function decideVerified(inputs) {
99
122
  const settlementOnly = declaredHeld &&
100
123
  inputs.unsettled !== undefined &&
101
124
  !openWrite &&
102
- contradictions.every((c) => c.kind === ContradictionKind.REQUEST_NEVER_SETTLED);
125
+ deciding.every((c) => c.kind === ContradictionKind.REQUEST_NEVER_SETTLED);
103
126
  // `signal-without-consequence` gets its own sentence, because the generic one is FALSE for it.
104
127
  //
105
128
  // That sentence says the window "closed before the app finished" and then explains that a poll or
@@ -108,8 +131,8 @@ export function decideVerified(inputs) {
108
131
  // explanation that told the agent to wait longer or assert something else, when the fact to act on
109
132
  // was that the app announced a consequence it did not deliver. A right verdict with a wrong reason
110
133
  // sends an agent to the wrong place, which costs as much as the wrong verdict did.
111
- const signalOnly = contradictions.length > 0 &&
112
- contradictions.every((c) => c.kind === ContradictionKind.SIGNAL_WITHOUT_CONSEQUENCE);
134
+ const signalOnly = deciding.length > 0 &&
135
+ deciding.every((c) => c.kind === ContradictionKind.SIGNAL_WITHOUT_CONSEQUENCE);
113
136
  if (signalOnly) {
114
137
  return {
115
138
  verified: Verified.UNKNOWN,
@@ -121,8 +144,8 @@ export function decideVerified(inputs) {
121
144
  'the store holds) before trusting it',
122
145
  };
123
146
  }
124
- if (contradictions.length > 0 && !settlementOnly) {
125
- const kinds = contradictions.map((c) => c.kind).join(', ');
147
+ if (deciding.length > 0 && !settlementOnly) {
148
+ const kinds = deciding.map((c) => c.kind).join(', ');
126
149
  return {
127
150
  verified: Verified.UNKNOWN,
128
151
  // NOT `unsettled`: this clause fires whether or not the page went idle, and naming it after
@@ -171,7 +194,10 @@ export function decideVerified(inputs) {
171
194
  return {
172
195
  verified: true === settled ? Verified.NO_FAULT : Verified.UNKNOWN,
173
196
  verifiedReason: VerifiedReason.ALREADY_TRUE,
174
- because: 'the declared consequence was already true before this action, so it proves nothing about it — assert something the action CHANGES (a signal, a request, a route, or store state)',
197
+ because: 'the declared consequence was already true before this action, so it proves nothing about it — assert something the action CHANGES (a signal, a request, a route, or store state)' +
198
+ (true === inputs.alreadyTrueHiddenMatch
199
+ ? '. That prior match was against a HIDDEN element — this predicate checks DOM presence, not visibility, by default; add `visible: true` if you meant "this is showing", not merely "this exists"'
200
+ : ''),
175
201
  };
176
202
  }
177
203
  // Dirty capture is NOT failure. The layer could not see part of the window, so any green is a
@@ -259,7 +285,8 @@ export function decideVerified(inputs) {
259
285
  verified: Verified.UNKNOWN,
260
286
  verifiedReason: VerifiedReason.OUTCOME_UNREAD,
261
287
  because: `a write returned 2xx with a response body that was never recorded (${outcomeUnread.join('; ')}), so its outcome is unread` +
262
- ' — a 200 describes the transport, not the result (a batch reports per-item failures in the body, and every GraphQL error is a 200). Enable it where your app calls connect(): `reticle.connect({ captureNetworkBodies: true })`, then re-run',
288
+ ' — a 200 describes the transport, not the result (a batch reports per-item failures in the body, and every GraphQL error is a 200). ' +
289
+ bodyCaptureRemedy(inputs.sdkVersion),
263
290
  };
264
291
  }
265
292
  // Never settled: the page may still be moving, so the observation window may have closed early.
@@ -1,17 +1,4 @@
1
- import { type ImpactCounts, type ImpactDefect, type ImpactScope, type ImpactSnapshot } from '@reticlehq/core';
2
- export interface ImpactPaths {
3
- project: string;
4
- global: string;
5
- }
6
- /**
7
- * Where this project's dashboard lives, if `reticle link` recorded one.
8
- *
9
- * Read from the link file rather than derived from the API url, because the API origin and the
10
- * console origin are different hosts in every deployment that is not a laptop. Synchronous and
11
- * best-effort, like every other read in this file: a missing or malformed link file means no link
12
- * in the HUD, never a failed tool call.
13
- */
14
- export declare function readDashboardUrl(reticleRoot: string): string | undefined;
1
+ import { type ImpactCounts, type ImpactDefect, type AccountState, type ImpactScope, type ImpactSnapshot } from '@reticlehq/core';
15
2
  /** Read a scope, tolerating absence and corruption: a broken file starts over rather than throwing. */
16
3
  export declare function readScope(path: string, now: number): ImpactScope;
17
4
  /** YYYY-MM-DD in local time - the day boundary a person recognises, not UTC's. */
@@ -46,6 +33,10 @@ export declare class ImpactStore {
46
33
  reticleRoot: string;
47
34
  projectName?: string;
48
35
  now?: () => number;
36
+ /** Where `~/.reticle` lives. Defaults to the real home; overridden so the shared scope is testable. */
37
+ globalRoot?: string;
38
+ /** Reads whether this machine is signed in. Injected so the store stays testable and pure-ish. */
39
+ account?: () => AccountState;
49
40
  });
50
41
  /** Notified after every fold, so the HUD can be pushed a fresh snapshot. */
51
42
  onChange(fn: () => void): void;
@@ -1,6 +1,7 @@
1
1
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
+ import { readAccountState } from '../cloud/account-state.js';
4
5
  import { ReticleDir, IMPACT_DAILY_BUCKETS, IMPACT_DEFECT_LIMIT, IMPACT_SCHEMA_VERSION, ImpactScopeSchema, addImpactCounts, emptyImpactCounts, emptyImpactRecords, estimateImpactSavings, } from '@reticlehq/core';
5
6
  /**
6
7
  * Where Reticle keeps the record of what it has done for you.
@@ -18,12 +19,16 @@ const WRITE_DEBOUNCE_MS = 800;
18
19
  const CLOUD_LINK_FILE = 'cloud.json';
19
20
  /**
20
21
  * `reticleRoot` is the project's own `.reticle` directory (what every other store here is handed);
21
- * the global scope always lives beside the daemon's own state in `~/.reticle`.
22
+ * the global scope lives beside the daemon's own state in `~/.reticle`.
23
+ *
24
+ * `globalRoot` defaults to the home directory and exists to be overridden. Reaching for `homedir()`
25
+ * implicitly made the machine-wide scope untestable — the one scope with a concurrency bug in it —
26
+ * so the seam is the fix's precondition, not a test affordance.
22
27
  */
23
- function impactPaths(reticleRoot) {
28
+ function impactPaths(reticleRoot, globalRoot) {
24
29
  return {
25
30
  project: join(reticleRoot, ReticleDir.IMPACT_FILE),
26
- global: join(homedir(), ReticleDir.ROOT, ReticleDir.IMPACT_FILE),
31
+ global: join(globalRoot, ReticleDir.ROOT, ReticleDir.IMPACT_FILE),
27
32
  };
28
33
  }
29
34
  /**
@@ -34,7 +39,7 @@ function impactPaths(reticleRoot) {
34
39
  * best-effort, like every other read in this file: a missing or malformed link file means no link
35
40
  * in the HUD, never a failed tool call.
36
41
  */
37
- export function readDashboardUrl(reticleRoot) {
42
+ function readDashboardUrl(reticleRoot) {
38
43
  try {
39
44
  const raw = JSON.parse(readFileSync(join(reticleRoot, CLOUD_LINK_FILE), 'utf8'));
40
45
  if ('object' !== typeof raw || null === raw)
@@ -157,10 +162,23 @@ export class ImpactStore {
157
162
  #dashboardUrl;
158
163
  #project;
159
164
  #global;
165
+ /**
166
+ * Deltas folded since the last machine-wide write, kept so the write can be a MERGE.
167
+ *
168
+ * The project scope has one writer and can be written wholesale. The machine-wide file is shared
169
+ * by every daemon on the box, so writing an in-memory copy taken at construction erases whatever a
170
+ * sibling wrote in between — which is what made the machine streak read LOWER than a single
171
+ * project's. Replaying these onto whatever is on disk at flush time is what makes the write additive.
172
+ */
173
+ #pendingGlobal = [];
160
174
  #timer;
161
175
  #onChange;
176
+ #account;
162
177
  constructor(opts) {
163
- this.#paths = impactPaths(opts.reticleRoot);
178
+ this.#paths = impactPaths(opts.reticleRoot, opts.globalRoot ?? homedir());
179
+ // Resolved per snapshot, not cached: a user who runs `reticle login` in another terminal must
180
+ // see the HUD change without restarting the daemon that is watching their app.
181
+ this.#account = opts.account ?? (() => readAccountState(homedir(), process.env));
164
182
  this.#now = opts.now ?? (() => Date.now());
165
183
  this.#projectName = opts.projectName;
166
184
  this.#dashboardUrl = readDashboardUrl(opts.reticleRoot);
@@ -175,7 +193,10 @@ export class ImpactStore {
175
193
  record(delta, meta = {}) {
176
194
  const now = this.#now();
177
195
  this.#project = applyDelta(this.#project, delta, now, meta);
196
+ // Folded in memory so a reader sees it immediately, AND buffered so the durable write can replay
197
+ // it onto a file a sibling may have moved on since. The in-memory copy is rebased at flush.
178
198
  this.#global = applyDelta(this.#global, delta, now, meta);
199
+ this.#pendingGlobal.push({ delta, now, meta });
179
200
  this.#scheduleFlush();
180
201
  this.#onChange?.();
181
202
  }
@@ -189,6 +210,7 @@ export class ImpactStore {
189
210
  snap.projectName = this.#projectName;
190
211
  if (this.#dashboardUrl !== undefined)
191
212
  snap.dashboardUrl = this.#dashboardUrl;
213
+ snap.account = this.#account();
192
214
  return snap;
193
215
  }
194
216
  /** Write now (process exit, tests). */
@@ -197,8 +219,29 @@ export class ImpactStore {
197
219
  clearTimeout(this.#timer);
198
220
  this.#timer = undefined;
199
221
  }
222
+ // One writer: the in-memory copy IS the truth.
200
223
  writeScope(this.#paths.project, this.#project);
201
- writeScope(this.#paths.global, this.#global);
224
+ this.#flushGlobal();
225
+ }
226
+ /**
227
+ * Publish this store's contribution to the machine-wide scope WITHOUT discarding anyone else's.
228
+ *
229
+ * Re-reads what is on disk and replays only the deltas recorded since the last write, so a sibling
230
+ * daemon's day survives. The merged result is adopted in memory too, which is what keeps the HUD's
231
+ * "everything on this machine" honest rather than showing this process's private view forever.
232
+ *
233
+ * ponytail: read-modify-write, not locked. Two daemons flushing inside the same read→rename window
234
+ * can still drop one batch — far rarer than the every-second-flush loss this replaces, and it costs
235
+ * one debounce window rather than a whole history. Upgrade to a lock file (exclusive `wx` create
236
+ * plus a stale timeout) if the counters are ever load-bearing for anything but the HUD.
237
+ */
238
+ #flushGlobal() {
239
+ if (0 === this.#pendingGlobal.length)
240
+ return;
241
+ const merged = this.#pendingGlobal.reduce((scope, p) => applyDelta(scope, p.delta, p.now, p.meta), readScope(this.#paths.global, this.#now()));
242
+ writeScope(this.#paths.global, merged);
243
+ this.#global = merged;
244
+ this.#pendingGlobal = [];
202
245
  }
203
246
  #scheduleFlush() {
204
247
  if (this.#timer !== undefined)
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { existsSync, readFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { resolveProjectCloud } from './cloud/cloud-config.js';
5
5
  import { startSyncDaemon } from './cloud/sync-daemon.js';
6
- import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, projectCandidates, } from '@reticlehq/core';
6
+ import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, projectCandidates, } from '@reticlehq/core/artifacts';
7
7
  import { discoverProjectConfigs } from './cli/config-discovery.js';
8
8
  import { projectCandidatesFrom, resolveArtifactRoot, } from './project/artifact-root.js';
9
9
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -1,4 +1,4 @@
1
- import { type RedactionPolicy, type ReticleEvent } from '@reticlehq/core';
1
+ import { URL_RAW, type RedactionPolicy, type ReticleEvent } from '@reticlehq/core';
2
2
  /**
3
3
  * CDP-authoritative network detail. Wherever the daemon has a CDP connection — whether it launched
4
4
  * the browser or attached to one someone else started — `page.on('response')` sees the FULL response:
@@ -9,7 +9,23 @@ import { type RedactionPolicy, type ReticleEvent } from '@reticlehq/core';
9
9
  * `page.on` attachment is thin Playwright glue (exercised e2e, like the pool launcher).
10
10
  */
11
11
  export interface NetworkDetail {
12
+ /**
13
+ * The URL as the agent reads it, redacted by the same rule the in-page observer applies.
14
+ *
15
+ * It arrives raw from the driver, exactly like the headers and the request body below, and it
16
+ * carries credentials just as routinely: a presigned upload, an OAuth callback, a single-use reset
17
+ * link. Redacting the other two and not this one left the one field nobody had to parse to read.
18
+ */
12
19
  url: string;
20
+ /**
21
+ * The URL before redaction, present only when redaction rewrote it.
22
+ *
23
+ * The grader-only match haystack, the same field and the same contract as on a NET_REQUEST: it is
24
+ * how `urlContains` still matches a public path segment the heuristic rewrote, and how the merge
25
+ * below pairs this detail with the request the SDK reported. `withoutUrlRaw` strips it before any
26
+ * event is rendered to an agent.
27
+ */
28
+ [URL_RAW]?: string;
13
29
  method?: string;
14
30
  status: number;
15
31
  headers: Record<string, string>;
@@ -24,6 +40,15 @@ export interface NetworkDetail {
24
40
  * window.fetch frame at all), rewrites requests invisibly. Available on the DRIVE path only.
25
41
  */
26
42
  requestBody?: string;
43
+ /**
44
+ * Present, and only ever `true`, when the bound above cut the captured body short.
45
+ *
46
+ * The same field and the same contract the in-page observer already emits. A shortened body an
47
+ * agent cannot tell apart from a whole one is the false green this project exists to prevent: a
48
+ * `net` assertion over a payload that was cut reads as an absence of the thing that was cut off.
49
+ * Omitted when the whole body fits, so the caveat means something when it appears.
50
+ */
51
+ requestBodyTruncated?: boolean;
27
52
  /**
28
53
  * The document that ISSUED the request, which is what identifies the session it belongs to.
29
54
  *