@reticlehq/server 3.1.0 → 3.3.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 (384) hide show
  1. package/SKILL.md +30 -28
  2. package/dist/command/cli/adhoc-suite.d.ts +19 -0
  3. package/dist/command/cli/adhoc-suite.js +88 -0
  4. package/dist/command/cli/adhoc-verdict.d.ts +23 -2
  5. package/dist/command/cli/adhoc-verdict.js +18 -10
  6. package/dist/command/cli/auth/device-flow.d.ts +2 -2
  7. package/dist/command/cli/cli-doctor.js +37 -5
  8. package/dist/command/cli/cli-flow-commands.d.ts +2 -1
  9. package/dist/command/cli/cli-kill.js +1 -1
  10. package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
  11. package/dist/command/cli/cli-parse-grammar.js +7 -0
  12. package/dist/command/cli/cli-parse-verify.d.ts +2 -5
  13. package/dist/command/cli/cli-parse-verify.js +86 -13
  14. package/dist/command/cli/cli-parse.d.ts +8 -1
  15. package/dist/command/cli/cli-parse.js +38 -7
  16. package/dist/command/cli/cli-verify.d.ts +59 -0
  17. package/dist/command/cli/cli-verify.js +131 -19
  18. package/dist/command/cli/cloud-cli.js +7 -6
  19. package/dist/command/cli/cloud-kit.d.ts +0 -2
  20. package/dist/command/cli/cloud-kit.js +3 -7
  21. package/dist/command/cli/config/config-discovery.d.ts +12 -0
  22. package/dist/command/cli/config/config-discovery.js +19 -0
  23. package/dist/command/cli/doctor/doctor-rows.d.ts +12 -0
  24. package/dist/command/cli/doctor/doctor-rows.js +12 -0
  25. package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
  26. package/dist/command/cli/drive/drive-command.js +1 -1
  27. package/dist/command/cli/launch/cli-launch.d.ts +3 -23
  28. package/dist/command/cli/launch/cli-launch.js +29 -70
  29. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +33 -0
  30. package/dist/command/cli/lifecycle/daemon-lifecycle.js +198 -0
  31. package/dist/command/cli/mcp-command.js +5 -3
  32. package/dist/command/cli/ports/resolve/cli-port.d.ts +15 -1
  33. package/dist/command/cli/ports/resolve/cli-port.js +50 -2
  34. package/dist/command/cli/report-command.d.ts +16 -0
  35. package/dist/command/cli/report-command.js +97 -0
  36. package/dist/command/cli/status/status-lines.d.ts +28 -0
  37. package/dist/command/cli/status/status-lines.js +117 -0
  38. package/dist/command/cli.d.ts +1 -1
  39. package/dist/command/cli.js +29 -177
  40. package/dist/command/daemon/binding/daemon-status-probe.d.ts +32 -0
  41. package/dist/command/daemon/binding/daemon-status-probe.js +93 -0
  42. package/dist/command/daemon/binding/loopback-reach.d.ts +42 -0
  43. package/dist/command/daemon/binding/loopback-reach.js +63 -0
  44. package/dist/command/daemon/binding/port-presence.d.ts +1 -1
  45. package/dist/command/daemon/daemon-resilience.d.ts +13 -1
  46. package/dist/command/daemon/daemon-resilience.js +12 -1
  47. package/dist/command/daemon/daemon.d.ts +2 -11
  48. package/dist/command/daemon/daemon.js +8 -11
  49. package/dist/command/daemon/idle-shutdown.d.ts +2 -1
  50. package/dist/command/daemon/lifetime/heartbeat.d.ts +8 -0
  51. package/dist/command/daemon/lifetime/heartbeat.js +10 -0
  52. package/dist/command/daemon/lifetime/log-cap.d.ts +68 -0
  53. package/dist/command/daemon/lifetime/log-cap.js +115 -0
  54. package/dist/command/setup/agent-notice-memory.d.ts +16 -0
  55. package/dist/command/setup/agent-notice-memory.js +65 -0
  56. package/dist/command/setup/bringup/dev-server-wait.d.ts +1 -0
  57. package/dist/command/setup/bringup/dev-server-wait.js +16 -0
  58. package/dist/command/setup/bringup/ensure-daemon.js +1 -1
  59. package/dist/command/setup/init/init-host.js +8 -0
  60. package/dist/command/setup/init/init-runtime.js +13 -1
  61. package/dist/command/setup/node-effects.js +1 -1
  62. package/dist/command/setup/probe/page-probe.js +9 -2
  63. package/dist/command/setup/run-setup.d.ts +25 -7
  64. package/dist/command/setup/run-setup.js +51 -9
  65. package/dist/command/setup/setup-command.js +31 -3
  66. package/dist/command/setup/terminal/confirm.js +2 -1
  67. package/dist/command/update/updater.d.ts +27 -0
  68. package/dist/command/update/updater.js +35 -2
  69. package/dist/features/crawl/crawl-tools.js +3 -1
  70. package/dist/features/crawl/crawl.d.ts +2 -0
  71. package/dist/features/crawl/crawl.js +3 -1
  72. package/dist/features/harness/drive-report.d.ts +73 -0
  73. package/dist/features/harness/drive-report.js +196 -0
  74. package/dist/features/harness/driver.js +29 -8
  75. package/dist/features/harness/drivers.d.ts +19 -0
  76. package/dist/features/harness/drivers.js +19 -0
  77. package/dist/features/harness/fill-values.d.ts +67 -0
  78. package/dist/features/harness/fill-values.js +153 -0
  79. package/dist/features/harness/jev-driver.d.ts +188 -0
  80. package/dist/features/harness/jev-driver.js +661 -0
  81. package/dist/features/harness/openai-driver.d.ts +46 -0
  82. package/dist/features/harness/openai-driver.js +197 -0
  83. package/dist/features/harness/platform-config.d.ts +62 -0
  84. package/dist/features/harness/platform-config.js +97 -0
  85. package/dist/features/visual/visual-tools.js +26 -2
  86. package/dist/index.d.ts +1 -1
  87. package/dist/index.js +80 -130
  88. package/dist/judgement/capsule/capsule-store.d.ts +31 -2
  89. package/dist/judgement/capsule/capsule-store.js +58 -6
  90. package/dist/judgement/capsule/capsule.js +5 -0
  91. package/dist/judgement/capsule/causal-summary.d.ts +13 -1
  92. package/dist/judgement/capsule/causal-summary.js +7 -0
  93. package/dist/judgement/domain/domain-model.js +38 -9
  94. package/dist/judgement/domain/domain-tools.js +4 -2
  95. package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
  96. package/dist/judgement/phenomena/phenomena.d.ts +2 -1
  97. package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
  98. package/dist/judgement/runs/artifact/gap-report.js +46 -0
  99. package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
  100. package/dist/judgement/runs/artifact/gap-summary.js +85 -0
  101. package/dist/judgement/runs/artifact/render-report.js +4 -1
  102. package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
  103. package/dist/judgement/runs/artifact/to-artifact.js +1 -1
  104. package/dist/judgement/runs/context-tools.js +21 -3
  105. package/dist/judgement/runs/drive-run.js +7 -2
  106. package/dist/judgement/runs/verification-sync.d.ts +2 -2
  107. package/dist/judgement/runs/verification-sync.js +9 -2
  108. package/dist/language/flows/affected-tools.js +6 -3
  109. package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
  110. package/dist/language/flows/annotate-notes/annotate.js +66 -18
  111. package/dist/language/flows/flow-anchor.d.ts +12 -0
  112. package/dist/language/flows/flow-anchor.js +20 -0
  113. package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
  114. package/dist/language/flows/flow-expect-grammar.js +92 -11
  115. package/dist/language/flows/flow-intent.js +1 -1
  116. package/dist/language/flows/flow-learning.d.ts +3 -0
  117. package/dist/language/flows/flow-learning.js +39 -8
  118. package/dist/language/flows/flow-replay-run.d.ts +55 -10
  119. package/dist/language/flows/flow-replay-run.js +217 -58
  120. package/dist/language/flows/flow-replay-types.d.ts +9 -0
  121. package/dist/language/flows/flow-replay.d.ts +29 -11
  122. package/dist/language/flows/flow-replay.js +221 -203
  123. package/dist/language/flows/flow-result.d.ts +18 -1
  124. package/dist/language/flows/flow-result.js +18 -2
  125. package/dist/language/flows/flow-step-runners.d.ts +26 -10
  126. package/dist/language/flows/flow-step-runners.js +20 -4
  127. package/dist/language/flows/flow-store-for-session.d.ts +2 -1
  128. package/dist/language/flows/flow-success.d.ts +21 -6
  129. package/dist/language/flows/flow-success.js +50 -138
  130. package/dist/language/flows/flow-tools.js +8 -4
  131. package/dist/language/flows/flows.d.ts +15 -10
  132. package/dist/language/flows/flows.js +17 -4
  133. package/dist/language/flows/heal.d.ts +9 -1
  134. package/dist/language/flows/heal.js +24 -2
  135. package/dist/language/flows/learned-sources.d.ts +21 -0
  136. package/dist/language/flows/learned-sources.js +84 -0
  137. package/dist/language/flows/narrow-write.d.ts +4 -4
  138. package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
  139. package/dist/language/flows/recording/recording-backtrack.js +1 -1
  140. package/dist/language/flows/recording/replay-halt.d.ts +2 -1
  141. package/dist/language/flows/recording/tape/recordings.d.ts +2 -2
  142. package/dist/language/flows/replay.js +13 -9
  143. package/dist/language/flows/stores/annotation-store.d.ts +5 -5
  144. package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
  145. package/dist/language/flows/stores/flake-store.js +2 -3
  146. package/dist/language/flows/suite/coverage.d.ts +1 -1
  147. package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
  148. package/dist/language/flows/suite-selection.d.ts +15 -1
  149. package/dist/language/flows/suite-selection.js +1 -1
  150. package/dist/language/flows/verify-change-tools.js +21 -4
  151. package/dist/memory/cloud/account-state.d.ts +1 -1
  152. package/dist/memory/cloud/account-state.js +5 -4
  153. package/dist/memory/cloud/cloud-config.d.ts +9 -0
  154. package/dist/memory/cloud/cloud-config.js +9 -0
  155. package/dist/memory/cloud/cloud-sync.d.ts +7 -4
  156. package/dist/memory/cloud/cloud-sync.js +11 -6
  157. package/dist/memory/cloud/harness-config.d.ts +12 -0
  158. package/dist/memory/cloud/harness-config.js +58 -0
  159. package/dist/memory/cloud/harness-offer.d.ts +44 -0
  160. package/dist/memory/cloud/harness-offer.js +146 -0
  161. package/dist/memory/cloud/harness-switch.d.ts +11 -0
  162. package/dist/memory/cloud/harness-switch.js +53 -0
  163. package/dist/memory/cloud/sync-cycle.d.ts +12 -0
  164. package/dist/memory/cloud/sync-cycle.js +29 -4
  165. package/dist/memory/cloud/sync-daemon.d.ts +35 -2
  166. package/dist/memory/cloud/sync-daemon.js +45 -0
  167. package/dist/memory/cloud/sync-disk.js +11 -6
  168. package/dist/memory/impact/impact-recorder.d.ts +8 -0
  169. package/dist/memory/impact/impact-recorder.js +2 -1
  170. package/dist/memory/impact/impact-store.d.ts +5 -0
  171. package/dist/memory/impact/impact-store.js +25 -0
  172. package/dist/memory/intent/inline-intent.js +8 -3
  173. package/dist/memory/intent/intent-shard-store.d.ts +33 -47
  174. package/dist/memory/intent/intent-shard-store.js +253 -141
  175. package/dist/memory/intent/intent-shard.d.ts +22 -23
  176. package/dist/memory/intent/intent-shard.js +1 -1
  177. package/dist/memory/intent/intent-store.d.ts +13 -8
  178. package/dist/memory/intent/intent-store.js +46 -90
  179. package/dist/memory/intent/intent-tools.js +34 -23
  180. package/dist/memory/journal/ambient-store.js +2 -3
  181. package/dist/memory/journal/attach-journal.d.ts +11 -0
  182. package/dist/memory/journal/attach-journal.js +1 -1
  183. package/dist/memory/journal/deviation-report.js +4 -1
  184. package/dist/memory/journal/deviation-service.js +10 -2
  185. package/dist/memory/journal/drive-flow.d.ts +2 -2
  186. package/dist/memory/journal/drive-flow.js +21 -12
  187. package/dist/memory/journal/envelope-store.js +2 -3
  188. package/dist/memory/journal/journal-query.d.ts +35 -1
  189. package/dist/memory/journal/journal-query.js +46 -0
  190. package/dist/memory/journal/journal-recorder.d.ts +71 -2
  191. package/dist/memory/journal/journal-recorder.js +45 -7
  192. package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
  193. package/dist/memory/journal/on-disk/retain-policy.js +44 -0
  194. package/dist/memory/journal/on-disk/retention.d.ts +91 -2
  195. package/dist/memory/journal/on-disk/retention.js +181 -11
  196. package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
  197. package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
  198. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -2
  199. package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -38
  200. package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
  201. package/dist/memory/journal/on-disk/workspace-tiers.js +88 -0
  202. package/dist/memory/journal/route-template.d.ts +22 -0
  203. package/dist/memory/journal/route-template.js +44 -0
  204. package/dist/memory/journal/self-observation.d.ts +3 -0
  205. package/dist/memory/journal/self-observation.js +67 -0
  206. package/dist/memory/journal/session-end.d.ts +17 -4
  207. package/dist/memory/journal/session-end.js +85 -7
  208. package/dist/memory/journal/session-journal.d.ts +122 -5
  209. package/dist/memory/journal/session-journal.js +489 -32
  210. package/dist/memory/project/artifact-root-resolver.d.ts +3 -0
  211. package/dist/memory/project/artifact-root-resolver.js +89 -0
  212. package/dist/memory/project/artifact-root.d.ts +39 -2
  213. package/dist/memory/project/artifact-root.js +58 -1
  214. package/dist/memory/project/baselines.d.ts +1 -1
  215. package/dist/memory/project/dir/fill-value-store.d.ts +32 -0
  216. package/dist/memory/project/dir/fill-value-store.js +88 -0
  217. package/dist/memory/project/dir/reticle-dir.d.ts +15 -5
  218. package/dist/memory/project/dir/reticle-dir.js +11 -0
  219. package/dist/memory/project/fs/fs-port.d.ts +22 -1
  220. package/dist/memory/project/fs/fs-port.js +72 -5
  221. package/dist/memory/project/fs/write-atomic.d.ts +26 -0
  222. package/dist/memory/project/fs/write-atomic.js +48 -0
  223. package/dist/memory/project/learned-routes.d.ts +16 -2
  224. package/dist/memory/project/learned-routes.js +26 -10
  225. package/dist/memory/project/project-for-root.d.ts +30 -0
  226. package/dist/memory/project/project-for-root.js +43 -0
  227. package/dist/memory/project/project-tools.js +10 -5
  228. package/dist/memory/project/session-root.d.ts +35 -6
  229. package/dist/memory/project/session-root.js +70 -8
  230. package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
  231. package/dist/memory/recall/prior/connection-memory.js +8 -0
  232. package/dist/memory/recall/registered-projects.d.ts +2 -1
  233. package/dist/portal/bridge/auth-failure-reason.d.ts +9 -0
  234. package/dist/portal/bridge/auth-failure-reason.js +20 -3
  235. package/dist/portal/bridge/bridge.d.ts +6 -1
  236. package/dist/portal/bridge/bridge.js +36 -2
  237. package/dist/portal/input/real-input.js +2 -1
  238. package/dist/portal/pool/browser-pool.d.ts +10 -0
  239. package/dist/portal/pool/browser-pool.js +10 -0
  240. package/dist/portal/pool/playwright-launcher.js +1 -0
  241. package/dist/portal/pool/pool-contract.d.ts +2 -0
  242. package/dist/portal/pool/storage-seed.d.ts +2 -1
  243. package/dist/portal/pool/zero-install.d.ts +27 -0
  244. package/dist/portal/pool/zero-install.js +54 -0
  245. package/dist/portal/realm/web-realm.js +6 -0
  246. package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
  247. package/dist/portal/session/dev-server/served-document.d.ts +21 -0
  248. package/dist/portal/session/dev-server/served-document.js +57 -0
  249. package/dist/portal/session/facts/background-traffic.d.ts +9 -0
  250. package/dist/portal/session/facts/background-traffic.js +25 -0
  251. package/dist/portal/session/facts/session-replaced.d.ts +19 -0
  252. package/dist/portal/session/facts/session-replaced.js +22 -0
  253. package/dist/portal/session/live-control-tools.js +48 -3
  254. package/dist/portal/session/no-session-diagnosis.d.ts +1 -0
  255. package/dist/portal/session/no-session-diagnosis.js +67 -60
  256. package/dist/portal/session/no-session-next-action.js +3 -2
  257. package/dist/portal/session/no-session-watch.js +14 -10
  258. package/dist/portal/session/presence/attachment-history.d.ts +13 -2
  259. package/dist/portal/session/rebind.d.ts +18 -0
  260. package/dist/portal/session/rebind.js +38 -0
  261. package/dist/portal/session/session-health.d.ts +61 -0
  262. package/dist/portal/session/session-health.js +40 -1
  263. package/dist/portal/session/session-info.d.ts +6 -0
  264. package/dist/portal/session/session-manager.d.ts +13 -0
  265. package/dist/portal/session/session-manager.js +42 -3
  266. package/dist/portal/session/session-successor.d.ts +11 -0
  267. package/dist/portal/session/session-successor.js +14 -1
  268. package/dist/portal/session/session-verdict-facts.d.ts +28 -0
  269. package/dist/portal/session/session-verdict-facts.js +15 -0
  270. package/dist/portal/session/session.d.ts +26 -10
  271. package/dist/portal/session/session.js +48 -49
  272. package/dist/status-payload.d.ts +12 -1
  273. package/dist/status-payload.js +4 -1
  274. package/dist/surface/http-server.js +3 -5
  275. package/dist/surface/mcp/faults/mcp-outage.d.ts +2 -1
  276. package/dist/surface/mcp/faults/mcp-outage.js +20 -4
  277. package/dist/surface/mcp/mcp-proxy.d.ts +10 -1
  278. package/dist/surface/mcp/mcp-proxy.js +23 -12
  279. package/dist/surface/mcp/mcp.d.ts +10 -1
  280. package/dist/surface/mcp/mcp.js +59 -29
  281. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +15 -2
  282. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +22 -5
  283. package/dist/surface/mcp/proxy/proxy-log.d.ts +5 -11
  284. package/dist/surface/mcp/proxy/proxy-log.js +7 -22
  285. package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
  286. package/dist/surface/mcp/server-instructions.js +12 -1
  287. package/dist/surface/tools/act/act-preflight.js +6 -1
  288. package/dist/surface/tools/act/already-true.d.ts +40 -0
  289. package/dist/surface/tools/act/already-true.js +36 -0
  290. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +6 -0
  291. package/dist/surface/tools/act/navigation/navigate-arrival.js +13 -0
  292. package/dist/surface/tools/act/sequence-grade.js +13 -0
  293. package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
  294. package/dist/surface/tools/act/settle-in-flight.js +29 -6
  295. package/dist/surface/tools/act-capsule.js +49 -8
  296. package/dist/surface/tools/act-tools.js +25 -24
  297. package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
  298. package/dist/surface/tools/args/numeric-bounds.js +30 -3
  299. package/dist/surface/tools/assert/assert-grade.js +14 -2
  300. package/dist/surface/tools/assert/assert-verdict.js +74 -21
  301. package/dist/surface/tools/browser-tools.js +4 -3
  302. package/dist/surface/tools/coverage-tools.js +5 -2
  303. package/dist/surface/tools/dynamic-tools.js +43 -46
  304. package/dist/surface/tools/error-recovery.d.ts +6 -0
  305. package/dist/surface/tools/error-recovery.js +15 -0
  306. package/dist/surface/tools/explore-tools.js +80 -7
  307. package/dist/surface/tools/feedback-tools.d.ts +2 -1
  308. package/dist/surface/tools/feedback-tools.js +50 -3
  309. package/dist/surface/tools/first-sentence.d.ts +24 -0
  310. package/dist/surface/tools/first-sentence.js +0 -0
  311. package/dist/surface/tools/harness-explore.d.ts +125 -1
  312. package/dist/surface/tools/harness-explore.js +401 -15
  313. package/dist/surface/tools/harness-plan.d.ts +76 -0
  314. package/dist/surface/tools/harness-plan.js +156 -0
  315. package/dist/surface/tools/harness-toolset.js +12 -1
  316. package/dist/surface/tools/invoke-tool.js +82 -9
  317. package/dist/surface/tools/lease-hint.d.ts +8 -0
  318. package/dist/surface/tools/lease-hint.js +10 -0
  319. package/dist/surface/tools/lease-tools.d.ts +16 -28
  320. package/dist/surface/tools/lease-tools.js +53 -5
  321. package/dist/surface/tools/lineage-tools.js +1 -1
  322. package/dist/surface/tools/live-call-text.d.ts +6 -0
  323. package/dist/surface/tools/live-call-text.js +36 -2
  324. package/dist/surface/tools/observe-tools.js +69 -15
  325. package/dist/surface/tools/read/query-windowed.d.ts +1 -1
  326. package/dist/surface/tools/read/query-windowed.js +1 -1
  327. package/dist/surface/tools/read-tools.js +1 -2
  328. package/dist/surface/tools/real-input-attempt.d.ts +0 -5
  329. package/dist/surface/tools/real-input-attempt.js +1 -1
  330. package/dist/surface/tools/tool-kit.d.ts +23 -3
  331. package/dist/surface/tools/tool-kit.js +6 -1
  332. package/dist/surface/tools/tool-surface.js +17 -5
  333. package/dist/surface/tools/tools-helpers.d.ts +8 -7
  334. package/dist/surface/tools/tools-helpers.js +11 -13
  335. package/dist/surface/tools/tools.js +70 -30
  336. package/dist/telemetry/bug-found.js +32 -2
  337. package/dist/telemetry/feedback-context.js +3 -3
  338. package/dist/telemetry/feedback-local.d.ts +1 -0
  339. package/dist/telemetry/feedback-local.js +1 -3
  340. package/dist/telemetry/feedback.d.ts +1 -1
  341. package/dist/telemetry/harness-drive.d.ts +6 -0
  342. package/dist/telemetry/harness-drive.js +39 -0
  343. package/dist/telemetry/hud-metrics.d.ts +32 -0
  344. package/dist/telemetry/hud-metrics.js +95 -0
  345. package/dist/telemetry/mcp-connection.d.ts +14 -1
  346. package/dist/telemetry/mcp-connection.js +17 -2
  347. package/dist/telemetry/onboarding-firsts.d.ts +30 -1
  348. package/dist/telemetry/onboarding-firsts.js +54 -12
  349. package/dist/telemetry/session-metrics.d.ts +5 -0
  350. package/dist/telemetry/session-metrics.js +15 -1
  351. package/dist/telemetry/verification-of.js +31 -3
  352. package/dist/wire-journal.d.ts +31 -0
  353. package/dist/wire-journal.js +100 -0
  354. package/docs/README.md +1 -0
  355. package/docs/agent-cheatsheet.md +7 -3
  356. package/docs/autodrive.md +42 -2
  357. package/docs/benchmarks.md +1 -1
  358. package/docs/capabilities.mdx +1 -1
  359. package/docs/cli/cloud.mdx +11 -9
  360. package/docs/cli/doctor.mdx +1 -0
  361. package/docs/cli/init.mdx +1 -0
  362. package/docs/cli/report.mdx +44 -0
  363. package/docs/cli/status.mdx +35 -20
  364. package/docs/cli.mdx +8 -18
  365. package/docs/desktop.mdx +1 -1
  366. package/docs/faq.mdx +1 -1
  367. package/docs/flows.md +11 -0
  368. package/docs/frameworks.mdx +113 -1
  369. package/docs/install-agentic.mdx +36 -0
  370. package/docs/install-manual.mdx +1 -1
  371. package/docs/packages/tauri.mdx +1 -1
  372. package/docs/packages.mdx +1 -1
  373. package/docs/predicates.mdx +144 -4
  374. package/docs/system-map.md +6 -4
  375. package/docs/telemetry.md +2 -1
  376. package/docs/token-efficiency.md +2 -2
  377. package/docs/tools/overview.mdx +1 -1
  378. package/docs/tools/query.mdx +10 -10
  379. package/docs/troubleshooting.mdx +8 -2
  380. package/docs/usage.md +2 -2
  381. package/docs/what-is-recorded.md +62 -0
  382. package/package.json +5 -5
  383. package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
  384. package/dist/judgement/outcome/predicate-to-expect.js +0 -148
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Which "add the reticle entry by hand" lines have already been said on this machine.
3
+ *
4
+ * Two commands register agents, deliberately: the installer does it because it once reached fewer
5
+ * clients than `init` and sent people to a project directory to finish a half-done job, and `init`
6
+ * does it because that is where the project gets wired. Both then narrate the clients whose config
7
+ * we refuse to rewrite -- a Zed settings file with comments in it, a Continue YAML somebody
8
+ * formatted -- so a `curl | sh` followed by `reticle init` prints the same two paragraphs twice in
9
+ * one sitting, and prints them again on every re-run after that, forever.
10
+ *
11
+ * The REGISTRATION stays in both places. The sentence is the thing that repeats, and it is a
12
+ * sentence about a MACHINE rather than about a run: a config we will not rewrite stays un-rewritten
13
+ * until somebody edits it, at which point the notice's own text changes and it is said again.
14
+ *
15
+ * Stamped by content, not by run. If the stamp cannot be read or written the notice simply prints,
16
+ * which is the failure that costs a duplicate line rather than a missed instruction.
17
+ */
18
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
19
+ import { join } from 'node:path';
20
+ /** Where the stamp lives, under the state home the caller resolves. */
21
+ export const NOTICE_MEMORY_BASENAME = 'agent-notices.json';
22
+ /** Identity of a notice: the same client, the same file, for the same reason. */
23
+ export function noticeKey(n) {
24
+ return JSON.stringify([n.name, n.file, n.why]);
25
+ }
26
+ /** Pure. The notices `said` has not already recorded, in order, without repeats. */
27
+ export function unsaidNotices(said, notices) {
28
+ const seen = new Set(said);
29
+ const out = [];
30
+ for (const n of notices) {
31
+ const key = noticeKey(n);
32
+ if (seen.has(key))
33
+ continue;
34
+ seen.add(key);
35
+ out.push(n);
36
+ }
37
+ return out;
38
+ }
39
+ /** Every key recorded before. An unreadable or malformed stamp reads as "nothing was said". */
40
+ export function readSaid(stateHome) {
41
+ try {
42
+ const raw = readFileSync(join(stateHome, NOTICE_MEMORY_BASENAME), 'utf8');
43
+ const parsed = JSON.parse(raw);
44
+ if (!Array.isArray(parsed))
45
+ return [];
46
+ return parsed.filter((k) => 'string' === typeof k);
47
+ }
48
+ catch {
49
+ return [];
50
+ }
51
+ }
52
+ /** Record these keys beside whatever was there. A failed write costs a repeated line, nothing more. */
53
+ export function rememberSaid(stateHome, keys) {
54
+ if (0 === keys.length)
55
+ return;
56
+ const merged = [...new Set([...readSaid(stateHome), ...keys])];
57
+ try {
58
+ mkdirSync(stateHome, { recursive: true });
59
+ writeFileSync(join(stateHome, NOTICE_MEMORY_BASENAME), JSON.stringify(merged, null, 2));
60
+ }
61
+ catch {
62
+ // Said once too often is the cost. Silence here is deliberate: a stamp that cannot be written
63
+ // must not fail an install.
64
+ }
65
+ }
@@ -56,6 +56,7 @@ export declare const WAIT_CEILING_MS: number;
56
56
  * that is a live server whatever its parent did.
57
57
  */
58
58
  export declare function judgeWait(facts: WaitFacts): WaitVerdict;
59
+ export declare function portBusyMessage(output: string): string | undefined;
59
60
  export declare function announcedUrl(output: string): string | undefined;
60
61
  /**
61
62
  * The url to watch: whatever the tool announced, else one composed from a port we OBSERVED our own
@@ -45,6 +45,22 @@ export function judgeWait(facts) {
45
45
  ? WaitVerdict.HUNG
46
46
  : WaitVerdict.WAITING;
47
47
  }
48
+ /**
49
+ * CRA (and webpack) print a prompt rather than a URL when their default port is taken, then exit
50
+ * or hang waiting for a tty. Setup used to report only "exited without serving anything", which
51
+ * hid the port that was actually busy (#802).
52
+ */
53
+ const CRA_PORT_BUSY = /Something is already running on port (\d+)/i;
54
+ const NAMED_PORT_BUSY = /Port (\d+) is (?:already )?in use/i;
55
+ const EADDRINUSE_PORT = /(?:EADDRINUSE|address already in use)[^\d]*(\d+)/i;
56
+ export function portBusyMessage(output) {
57
+ const match = CRA_PORT_BUSY.exec(output) ?? NAMED_PORT_BUSY.exec(output) ?? EADDRINUSE_PORT.exec(output);
58
+ const port = match?.[1];
59
+ if (undefined === port)
60
+ return undefined;
61
+ return (`port ${port} is already in use — something else is serving there, so this launcher did not ` +
62
+ `bind. Pass --url http://localhost:${port} if that process is this app, or free the port and re-run.`);
63
+ }
48
64
  /** A url the dev server announced, if it has announced one. Never composed, only read. */
49
65
  const URL_IN_OUTPUT = /https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\]):\d+/;
50
66
  export function announcedUrl(output) {
@@ -16,7 +16,7 @@ import { spawnDaemon } from '../../daemon/daemon.js';
16
16
  import { probePresence, presenceIsUsable } from '../../daemon/binding/port-presence.js';
17
17
  import { waitForDaemon, probeDaemon } from '../../../surface/mcp/mcp-proxy.js';
18
18
  import { daemonSpawnArgs } from '../../cli/daemon-start-options.js';
19
- import { fetchStatus } from '../../cli/launch/cli-launch.js';
19
+ import { fetchStatus } from '../../daemon/binding/daemon-status-probe.js';
20
20
  export const EnsureDaemon = {
21
21
  /** Something usable was already there. */
22
22
  ADOPTED: 'adopted',
@@ -3,6 +3,9 @@ import { reportInitOutcome } from '../../../telemetry/init-telemetry.js';
3
3
  import { reportOnboardingStep } from '../../../telemetry/onboarding-funnel.js';
4
4
  import { defaultPairingTokenDir, readOrCreatePairingTokenSync, } from '../../../portal/bridge/pairing-token.js';
5
5
  import { declaredInstallSource } from '../../../telemetry/install-source.js';
6
+ /** The variable Claude Code sets on every process it spawns, and the value it sets it to. */
7
+ const CLAUDE_CODE_MARKER_ENV = 'CLAUDECODE';
8
+ const CLAUDE_CODE_MARKER_VALUE = '1';
6
9
  export function serverInitHost() {
7
10
  return {
8
11
  span(name, fields, fn) {
@@ -33,5 +36,10 @@ export function serverInitHost() {
33
36
  installSource() {
34
37
  return declaredInstallSource();
35
38
  },
39
+ // Claude Code marks every process it spawns. If the marker ever changes, init falls back to the
40
+ // notice it printed before, which is the safe direction.
41
+ insideClaudeCode() {
42
+ return CLAUDE_CODE_MARKER_VALUE === process.env[CLAUDE_CODE_MARKER_ENV];
43
+ },
36
44
  };
37
45
  }
@@ -15,7 +15,7 @@ import { relaunchDecision } from '../bringup/relaunch.js';
15
15
  import { claudeTranscriptExists, codexSessionFor } from '../terminal/transcripts.js';
16
16
  import { probePresence } from '../../daemon/binding/port-presence.js';
17
17
  import { probeDaemon } from '../../../surface/mcp/proxy/proxy-daemon-probe.js';
18
- import { fetchStatus } from '../../cli/launch/cli-launch.js';
18
+ import { fetchStatus } from '../../daemon/binding/daemon-status-probe.js';
19
19
  import { collectEnv, DEFAULT_PHASE_TIMEOUT_MS } from '../setup-options.js';
20
20
  /** How often the runtime phases look again: fast enough not to be the wait, slow enough to be free. */
21
21
  const POLL_MS = 250;
@@ -129,6 +129,18 @@ export async function continueAfterInit(parsed, result, io, cwd) {
129
129
  return;
130
130
  }
131
131
  io.print('');
132
+ // The precondition for everything the closing asks for, printed on the path that could not say
133
+ // it. An agent client reads its MCP server list when it STARTS and never re-reads it, so on the
134
+ // run that first registers Reticle the `reticle_*` tools are not in the session that just asked
135
+ // for them -- and this path closes by telling that session to call `reticle_act_and_wait`.
136
+ // `restartHint` has said this for a long time and is printed only when init stops at the files.
137
+ if (true === result.mcpNewlyRegistered) {
138
+ io.print('Reticle was registered with your agent by this run, and an agent reads its tool list only ' +
139
+ 'when it starts: the `reticle_*` tools are NOT in this session yet. Restart it first — ' +
140
+ '`npx @reticlehq/server init --relaunch` prints the exact resume command for Claude Code ' +
141
+ 'and Codex. Once per machine.');
142
+ io.print('');
143
+ }
132
144
  if (outcome.ok && !outcome.flowSaved) {
133
145
  // Success, and no flow. Saying "a flow was driven" here would replace a wrong exit code with
134
146
  // a wrong sentence, which is the worse of the two: the exit code is read by CI and the
@@ -15,7 +15,7 @@ import { spawn, spawnSync } from 'node:child_process';
15
15
  import { Socket } from 'node:net';
16
16
  import { existsSync, readdirSync } from 'node:fs';
17
17
  import { join } from 'node:path';
18
- import { fetchStatus } from '../cli/launch/cli-launch.js';
18
+ import { fetchStatus } from '../daemon/binding/daemon-status-probe.js';
19
19
  import { descendants, parseLsofPorts, parseNetstatListeners, parseWmicProcesses, } from './terminal/listeners.js';
20
20
  import { loopbackProbeUrls } from './probe/loopback-probe-urls.js';
21
21
  const WINDOWS = 'win32' === process.platform;
@@ -43,8 +43,15 @@ export function describePage(finding, url) {
43
43
  return (`${url} is served, and the SDK is NOT in the page. That is almost always a dev server still ` +
44
44
  'serving the bundle it built before the build config was edited: restart it and hard-reload.');
45
45
  case PageFinding.SDK_PRESENT:
46
- return (`The SDK IS in the page at ${url} and never dialled the bridge, so it loaded and returned ` +
47
- 'early: a localhost guard, a production-only check, or a bridge port that differs on the two sides.');
46
+ // This used to end "so it loaded and returned early", which is a claim about a code path this
47
+ // check cannot see — and at least one real occurrence was the opposite. On a Windows install
48
+ // gate cell the page console read "could not open a websocket to ws://localhost:<port>, 3
49
+ // attempts, all failed": the SDK dialled three times and the socket failed, having returned
50
+ // early from nothing. What is knowable here is that the SDK is present and no session arrived.
51
+ return (`The SDK IS in the page at ${url} and never dialled the bridge: a localhost guard, a ` +
52
+ 'production-only check, a bridge port that differs on the two sides, or `localhost` ' +
53
+ 'resolving to IPv6 where the daemon is only on IPv4 — `npx @reticlehq/server doctor` names ' +
54
+ 'that last one, and the page console names the first three.');
48
55
  }
49
56
  }
50
57
  /**
@@ -39,6 +39,18 @@ export interface SetupOutcome {
39
39
  readonly notes: string[];
40
40
  }
41
41
  /** Everything the sequence needs from the world, so none of it is reached for directly. */
42
+ /**
43
+ * The daemon's reason for "no session", in the two lengths its two readers need.
44
+ *
45
+ * Same fact, not two facts: `full` is the lead plus the differential behind it, so a reader given
46
+ * only one of them is never given something the other contradicts.
47
+ */
48
+ export interface DaemonReason {
49
+ /** One line, printed to the person watching the install. */
50
+ readonly lead: string;
51
+ /** The whole differential, recorded for the agent reading `--json`. Defaults to the lead. */
52
+ readonly full?: string;
53
+ }
42
54
  export interface SetupEffects {
43
55
  /** Start the dev server. Resolves once started; the caller owns stopping it. */
44
56
  readonly startDevServer: (command: string, cwd: string) => Promise<void>;
@@ -70,6 +82,19 @@ export interface SetupEffects {
70
82
  */
71
83
  readonly openBrowser: (url: string) => Promise<void>;
72
84
  readonly listSessions: () => Promise<CandidateSession[]>;
85
+ /**
86
+ * The daemon's own account of why nothing connected, if it can be asked.
87
+ *
88
+ * The daemon is the only party that can see a hello it REFUSED, and a refusal is the one piece of
89
+ * positive evidence in the whole failure -- only an SDK dials the bridge, so a turned-away page
90
+ * proves the app is running, instrumented and pointed here. `page-probe.ts` has said since it was
91
+ * written that this sentence is the one to lead with and that the page finding merely adds the
92
+ * page-side fact; only the second half was wired.
93
+ *
94
+ * Optional, and allowed to fail: setup must still report what it saw when the daemon cannot be
95
+ * reached, which is itself one of the states this runs in.
96
+ */
97
+ readonly daemonWhy?: () => Promise<DaemonReason | undefined>;
73
98
  readonly now: () => number;
74
99
  readonly sleep: (ms: number) => Promise<void>;
75
100
  readonly note: (line: string) => void;
@@ -95,11 +120,4 @@ export interface SetupInput {
95
120
  readonly phaseTimeoutMs: number;
96
121
  readonly pollMs: number;
97
122
  }
98
- /**
99
- * Run the phases. Stops at the first one that cannot continue, and always says what is left.
100
- *
101
- * A phase that fails is not an error to throw: the caller is usually an agent, and a thrown
102
- * exception loses the four things it needs — how far this got, the url, the session, and what to do
103
- * about it.
104
- */
105
123
  export declare function runSetupPhases(input: SetupInput, fx: SetupEffects): Promise<SetupOutcome>;
@@ -13,7 +13,7 @@
13
13
  * way of going wrong, and the alternative to injecting them is a test that boots a real dev server
14
14
  * and a real browser to find out what happens when neither works.
15
15
  */
16
- import { judgeWait, QUIET_MEANS_HUNG_MS, urlToWatch, WaitVerdict, } from './bringup/dev-server-wait.js';
16
+ import { judgeWait, portBusyMessage, QUIET_MEANS_HUNG_MS, urlToWatch, WaitVerdict, } from './bringup/dev-server-wait.js';
17
17
  import { waitProgressLine } from './terminal/wait-progress.js';
18
18
  /**
19
19
  * Windows gets longer to say something before silence counts as a wedge.
@@ -69,6 +69,23 @@ const stop = (input, phase, partial, notes) => ({
69
69
  * exception loses the four things it needs — how far this got, the url, the session, and what to do
70
70
  * about it.
71
71
  */
72
+ /**
73
+ * Ask the daemon why, and treat every failure as "it did not say".
74
+ *
75
+ * A daemon that cannot be reached is one of the states this runs in, so an error here is data, not
76
+ * an exception: the page finding below still prints and the run still ends with its own verdict.
77
+ */
78
+ async function daemonWhy(fx) {
79
+ if (undefined === fx.daemonWhy)
80
+ return undefined;
81
+ try {
82
+ const why = await fx.daemonWhy();
83
+ return undefined === why || 0 === why.lead.length ? undefined : why;
84
+ }
85
+ catch {
86
+ return undefined;
87
+ }
88
+ }
72
89
  export async function runSetupPhases(input, fx) {
73
90
  const notes = [];
74
91
  const note = (line) => {
@@ -139,7 +156,8 @@ export async function runSetupPhases(input, fx) {
139
156
  break;
140
157
  }
141
158
  if (WaitVerdict.DEAD === verdict) {
142
- note('The dev server exited without serving anything.');
159
+ note(portBusyMessage(fx.devServerOutput()) ??
160
+ 'The dev server exited without serving anything.');
143
161
  return stop(input, SetupPhase.DEV_SERVER, {}, notes);
144
162
  }
145
163
  if (WaitVerdict.HUNG === verdict) {
@@ -238,7 +256,25 @@ export async function runSetupPhases(input, fx) {
238
256
  'the capture helper not installed, or a CSP that blocks the bridge: run `npx @reticlehq/server doctor`.');
239
257
  }
240
258
  else {
241
- // What the page contains is the one thing the daemon cannot know, so it is worth one fetch.
259
+ // The daemon first, when it has something to say: it is the only party that can see a hello it
260
+ // refused, and that outranks anything inferred from an absence. Then the page, which is the
261
+ // one thing the daemon cannot know, and worth one fetch.
262
+ const why = await daemonWhy(fx);
263
+ if (undefined !== why) {
264
+ // Two readers, two lengths, one fetch.
265
+ //
266
+ // The person watching an install gets the LEAD — that is what trimming this diagnosis was
267
+ // for. The agent reads `notes` out of `--json`, and what it acts on is the differential:
268
+ // which ports were actually scanned (so an empty result is not proof), why a missing
269
+ // `.reticle.json` is expected in a monorepo, and the lease that opens a URL on a box with
270
+ // no browser at all.
271
+ //
272
+ // Printing only the lead put BOTH readers on the lead, which silently emptied the agent
273
+ // surface: `init --json` stopped mentioning the lease, and `break/break-matrix.mjs`
274
+ // (`no-browser-to-open`) went red for exactly that reason.
275
+ fx.note(why.lead);
276
+ notes.push(why.full ?? why.lead);
277
+ }
242
278
  note(describePage(readPage(await fx.probePage(url)), url));
243
279
  }
244
280
  return stop(input, SetupPhase.CONNECT, { url }, notes);
@@ -256,12 +292,18 @@ export async function runSetupPhases(input, fx) {
256
292
  // Both routes, because `explore` needs a model: without ANTHROPIC_API_KEY it answers "No model
257
293
  // configured to drive the app". Naming only that one hands the reader a dead end on any machine
258
294
  // without a key, which is the same defect this release spent its time removing everywhere else.
259
- note(`Connected, and the app at ${url} is instrumented. Onboarding is done. Nothing is PROVED ` +
260
- 'yet: that is the first run, and it is yours to start. Drive one flow yourself with the ' +
261
- '`reticle_*` tools, ending in `reticle_act_and_wait` or `reticle_assert` — those two are ' +
262
- 'what produce a verdict. Or hand the whole drive to Reticle with `reticle_verify { action: ' +
263
- '"explore", persona: "<who does what>" }`, which records what it drove so later runs replay ' +
264
- 'with no model in the loop; that route needs ANTHROPIC_API_KEY.');
295
+ // Three short lines rather than one paragraph, and each says who it is for.
296
+ //
297
+ // This was a single 524-character note, measured on a real first run: eighty-three words, no
298
+ // break, naming `reticle_*` tools at a reader who had just typed `reticle init` in a terminal and
299
+ // has no such tools to call. The facts were right and the shape made them unreadable, which on
300
+ // the last line of onboarding is the same as not saying them.
301
+ note(`Connected. ${url} is instrumented. Onboarding is done; nothing is verified yet.`);
302
+ note('Agent: drive one flow and end it with `reticle_act_and_wait` or `reticle_assert`. Those two ' +
303
+ 'produce a verdict; nothing else does.');
304
+ note('Or hand over the whole drive: `reticle_verify { action: "explore", persona: "<who does ' +
305
+ 'what>" }` records what it drove, so later runs replay with no model in the loop. Needs ' +
306
+ 'ANTHROPIC_API_KEY.');
265
307
  return {
266
308
  ok: true,
267
309
  reachedPhase: SetupPhase.CONNECT,
@@ -22,7 +22,10 @@ import { reticleStateHome } from '../daemon/daemon.js';
22
22
  import { readDevServers } from '../daemon/dev-servers.js';
23
23
  import { urlOfExistingApp } from './probe/existing-app.js';
24
24
  import { listSessions, OwnedDevServer, probePage } from './node-effects.js';
25
+ import { fetchStatus } from '../daemon/binding/daemon-status-probe.js';
26
+ import { summarizeStatus } from '../cli/launch/cli-launch.js';
25
27
  import { runSetupPhases, } from './run-setup.js';
28
+ import { noticeKey, readSaid, rememberSaid, unsaidNotices } from './agent-notice-memory.js';
26
29
  /**
27
30
  * The dev server a crash should take with it, if one is running right now.
28
31
  *
@@ -89,10 +92,20 @@ export function registerOtherAgents(print) {
89
92
  if (0 < wrote.length) {
90
93
  print(`registered the MCP server with ${wrote.length} more agent(s): ${wrote.map((r) => r.name).join(', ')}`);
91
94
  }
92
- // A format we will not rewrite is somebody's to edit, so it has to be said rather than skipped.
93
- for (const manual of results.filter((r) => 'manual' === r.action)) {
94
- print(`${manual.name}: ${manual.why} — add the reticle entry to ${manual.file} by hand.`);
95
+ // A format we will not rewrite is somebody's to edit, so it has to be said rather than skipped --
96
+ // but said ONCE. Two commands call this function by design, so a `curl | sh` followed by
97
+ // `reticle init` printed the same two paragraphs twice in one sitting, and again on every re-run
98
+ // after that. See agent-notice-memory.ts: the stamp is keyed on the notice's own text, so a
99
+ // config that changes is reported again.
100
+ const stateHome = reticleStateHome();
101
+ const manual = results
102
+ .filter((r) => 'manual' === r.action)
103
+ .map((r) => ({ name: r.name, file: r.file, why: r.why }));
104
+ const fresh = unsaidNotices(readSaid(stateHome), manual);
105
+ for (const notice of fresh) {
106
+ print(`${notice.name}: ${notice.why} — add the reticle entry to ${notice.file} by hand.`);
95
107
  }
108
+ rememberSaid(stateHome, fresh.map((n) => noticeKey(n)));
96
109
  const skills = applyAgentSkills(agentIo, { home, platform });
97
110
  if (0 < skills.length)
98
111
  print(`wrote the /reticle skill for ${skills.length} agent(s)`);
@@ -166,6 +179,21 @@ export async function runSetupCommand(input, print) {
166
179
  }
167
180
  },
168
181
  listSessions: () => listSessions(input.bridgePort),
182
+ // `summarizeStatus` already narrows this payload for `reticle status`; reusing it here keeps one
183
+ // reader of the wire shape rather than two that can disagree about which key carries the reason.
184
+ // Both lengths, from the one read: the lead is printed to the person watching the install, the
185
+ // full differential is recorded for the agent reading `--json`. Returning only the lead here is
186
+ // what emptied the agent surface — see the note at the call site in `run-setup.ts`.
187
+ daemonWhy: async () => {
188
+ const status = summarizeStatus(await fetchStatus(input.bridgePort));
189
+ const full = status.why;
190
+ // A daemon too old to report the short one falls back to the long one, because saying the
191
+ // right thing at length beats saying nothing.
192
+ const lead = status.whyLead ?? full;
193
+ if (undefined === lead)
194
+ return undefined;
195
+ return undefined === full ? { lead } : { lead, full };
196
+ },
169
197
  now: () => Date.now(),
170
198
  sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
171
199
  note: print,
@@ -29,7 +29,8 @@
29
29
  * it worked.
30
30
  */
31
31
  import { InitConfirmation, RETICLE_DEFAULT_PORT } from '@reticlehq/core';
32
- import { fetchStatus, summarizeStatus } from '../../cli/launch/cli-launch.js';
32
+ import { summarizeStatus } from '../../cli/launch/cli-launch.js';
33
+ import { fetchStatus } from '../../daemon/binding/daemon-status-probe.js';
33
34
  import { readDevServers } from '../../daemon/dev-servers.js';
34
35
  import { devServersForProject } from '@reticlehq/core/artifacts';
35
36
  import { readProjectId } from '../../cli/ports/resolve/cli-port.js';
@@ -29,6 +29,33 @@ type ExecutionKind = (typeof ExecutionKind)[keyof typeof ExecutionKind];
29
29
  * `apply_update` npm-installs into the user's own project (polluting their package.json).
30
30
  */
31
31
  export declare function classifyExecutionKind(script: string): ExecutionKind;
32
+ /**
33
+ * Spawn npm, through a shell on Windows because `npm.cmd` cannot be spawned without one.
34
+ *
35
+ * Since the CVE-2024-27980 fix -- in every supported Node -- spawning a `.cmd` with no shell throws
36
+ * EINVAL. This repo has already paid for that lesson once, at `init/src/node-io.ts`: `reticle setup
37
+ * mcp` probed for the Claude CLI with a bare `execFileSync`, the probe threw, and the installer
38
+ * told the user their machine had no Claude Code on it. The same shape sat here, in the SELF-UPDATE
39
+ * path -- the channel every other fix reaches a user through, and the one place a silent failure
40
+ * strands somebody on an old version indefinitely.
41
+ *
42
+ * Arguments are quoted with init's own rule rather than a second copy of it. They are internal
43
+ * today (`install`, `-g`, and a package spec whose version comes from the registry), but a shell
44
+ * turns "internal" into "whatever the registry said", and the quoting is one import.
45
+ */
46
+ export declare function npmSpawn(args: string[],
47
+ /**
48
+ * Injected so the Windows branch is testable on every platform.
49
+ *
50
+ * The bug this fixes could only be OBSERVED on Windows, and a check that can only fail on a
51
+ * machine nobody here runs is a check that fails in CI at the earliest and in the field at the
52
+ * latest. The rule is a pure function of the platform string, so it is one.
53
+ */
54
+ on?: string): {
55
+ file: string;
56
+ argv: string[];
57
+ shell: boolean;
58
+ };
32
59
  interface NpmInstall {
33
60
  args: string[];
34
61
  cwd?: string;
@@ -6,6 +6,7 @@ import { dirname, join } from 'node:path';
6
6
  import { loadManifest, saveManifest } from './update-checker.js';
7
7
  import { RETICLE_NPM_PACKAGE, SERVER_VERSION } from '../version/identity/server-version.js';
8
8
  import { log } from '../../log.js';
9
+ import { windowsShellArg } from '@reticlehq/init';
9
10
  import { TelemetryEventKind } from '@reticlehq/core/telemetry';
10
11
  import { getTelemetry } from '../../telemetry/telemetry.js';
11
12
  import { wasNudged } from './nudge-credit.js';
@@ -34,7 +35,6 @@ export async function reportVersionChange(from, to, direction) {
34
35
  /* best-effort, like every other send */
35
36
  }
36
37
  }
37
- const NPM_BIN = NodePlatform.WINDOWS === platform() ? 'npm.cmd' : 'npm';
38
38
  const NPM_TIMEOUT_MS = 120_000;
39
39
  /** How this reticle process was launched — determines which npm strategy to use for updates. */
40
40
  const ExecutionKind = {
@@ -91,9 +91,42 @@ function findLocalProjectRoot() {
91
91
  dir = parent;
92
92
  }
93
93
  }
94
+ /**
95
+ * Spawn npm, through a shell on Windows because `npm.cmd` cannot be spawned without one.
96
+ *
97
+ * Since the CVE-2024-27980 fix -- in every supported Node -- spawning a `.cmd` with no shell throws
98
+ * EINVAL. This repo has already paid for that lesson once, at `init/src/node-io.ts`: `reticle setup
99
+ * mcp` probed for the Claude CLI with a bare `execFileSync`, the probe threw, and the installer
100
+ * told the user their machine had no Claude Code on it. The same shape sat here, in the SELF-UPDATE
101
+ * path -- the channel every other fix reaches a user through, and the one place a silent failure
102
+ * strands somebody on an old version indefinitely.
103
+ *
104
+ * Arguments are quoted with init's own rule rather than a second copy of it. They are internal
105
+ * today (`install`, `-g`, and a package spec whose version comes from the registry), but a shell
106
+ * turns "internal" into "whatever the registry said", and the quoting is one import.
107
+ */
108
+ export function npmSpawn(args,
109
+ /**
110
+ * Injected so the Windows branch is testable on every platform.
111
+ *
112
+ * The bug this fixes could only be OBSERVED on Windows, and a check that can only fail on a
113
+ * machine nobody here runs is a check that fails in CI at the earliest and in the field at the
114
+ * latest. The rule is a pure function of the platform string, so it is one.
115
+ */
116
+ on = platform()) {
117
+ const bin = NodePlatform.WINDOWS === on ? 'npm.cmd' : 'npm';
118
+ if (NodePlatform.WINDOWS !== on)
119
+ return { file: bin, argv: args, shell: false };
120
+ return { file: [bin, ...args.map(windowsShellArg)].join(' '), argv: [], shell: true };
121
+ }
94
122
  function runNpm(args, opts = {}) {
123
+ const spawn = npmSpawn(args);
95
124
  return new Promise((resolve, reject) => {
96
- execFile(NPM_BIN, args, { timeout: NPM_TIMEOUT_MS, ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}) }, (err, _stdout, stderr) => {
125
+ execFile(spawn.file, spawn.argv, {
126
+ timeout: NPM_TIMEOUT_MS,
127
+ ...(spawn.shell ? { shell: true } : {}),
128
+ ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),
129
+ }, (err, _stdout, stderr) => {
97
130
  if (err !== null) {
98
131
  reject(new Error(`npm ${args.join(' ')} failed: ${stderr !== '' ? stderr : err.message}`));
99
132
  }
@@ -1,4 +1,6 @@
1
1
  import { z } from 'zod';
2
+ import { sessionRoot } from '../../memory/project/session-root.js';
3
+ import { projectForRoot } from '../../memory/project/project-for-root.js';
2
4
  import { ReticleTool } from '@reticlehq/core';
3
5
  import { asNumber, asString } from '@reticlehq/core';
4
6
  import { stepCountSchema, timeoutMsSchema } from '../../surface/tools/args/numeric-bounds.js';
@@ -80,7 +82,7 @@ export const CRAWL_TOOLS = [
80
82
  ...routesFromEvents(session.eventsSince(since)),
81
83
  ];
82
84
  if (routes.length > 0)
83
- await deps.project.recordRoutes(routes);
85
+ await projectForRoot(deps, sessionRoot(deps, session.id)).recordRoutes(routes);
84
86
  return report;
85
87
  },
86
88
  },
@@ -16,6 +16,8 @@ export interface CrawlSession {
16
16
  /** The page under test, so a third-party beacon cannot be reported against a control. Optional
17
17
  * for the same reason `currentDocumentId` is. */
18
18
  readonly url?: string | undefined;
19
+ /** Same-origin endpoints the project declared as background. Optional like `url`. */
20
+ readonly background?: readonly string[] | undefined;
19
21
  /**
20
22
  * Is the page BACKGROUNDED? A hidden tab has its rAF clamped and the DOM observer flushes on rAF,
21
23
  * so real renders emit nothing and every absence-derived contradiction reads that silence as a
@@ -8,7 +8,8 @@ import { span } from '../../trace.js';
8
8
  import { ActionType, CRAWL_DEFAULTS, CrawlAnomalyKind, DANGEROUS_ACTION_CONFIRM_ARG, EventType, ReticleCommand, } from '@reticlehq/core';
9
9
  import { crawlEmptyNote } from './crawl-empty.js';
10
10
  import { asNumber, asRecord, asString } from '@reticlehq/core';
11
- import { parseInteractive, sourceOf } from '../../surface/tools/tools-helpers.js';
11
+ import { parseInteractive } from '@reticlehq/core';
12
+ import { sourceOf } from '../../surface/tools/tools-helpers.js';
12
13
  import { isSessionReplacedError } from '../../portal/session/facts/session-replaced.js';
13
14
  import { ReticleTool } from '@reticlehq/core';
14
15
  import { findContradictions } from '@reticlehq/engine/disagreement/contradictions.js';
@@ -346,6 +347,7 @@ export async function crawl(session, opts, sleep) {
346
347
  currentDocumentId: session.currentDocumentId,
347
348
  currentEditEpoch: session.currentEditEpoch,
348
349
  appOrigin: session.url,
350
+ background: session.background,
349
351
  // A hidden tab cannot be observed, so its silence is not evidence. Stated from the session
350
352
  // rather than left to a heartbeat landing inside a 300ms window.
351
353
  pageHidden: session.throttled?.(),
@@ -0,0 +1,73 @@
1
+ /**
2
+ * What a drive did, written from the evidence rather than narrated by the model that drove.
3
+ *
4
+ * `HarnessResult.summary` is the driver's own account, and it has always carried a warning that
5
+ * nothing downstream grades it. That was a caveat when the driver was a language model. It became a
6
+ * hole the moment a System One model could drive: Jev answers typed questions and cannot produce a
7
+ * sentence, so `summary` came back EMPTY, and an agent calling `explore` got a result with no
8
+ * account of what had happened in it at all.
9
+ *
10
+ * Restoring prose would have been the obvious fix and the wrong one. The drive already records
11
+ * every call it made, with the arguments it used and the verdict the engine returned — so the
12
+ * account can be DERIVED. That is strictly better than a narration: a model summarising its own
13
+ * drive is the one witness with a reason to round "unknown" up to "worked", and this cannot, because
14
+ * it is not writing about the drive, it is reading it.
15
+ *
16
+ * Written for the agent on the other end of the MCP call. It leads with what was proved, because
17
+ * that is what a coding agent has to gate on, and it says "not proved" in those words rather than
18
+ * leaving a verdict of `unknown` to be read as a pass.
19
+ */
20
+ import type { ToolOutcome } from './harness.js';
21
+ /** One driven action, reduced to what a reader needs: what was done, and what it proved. */
22
+ export interface DrivenStep {
23
+ /** The action verb, e.g. `click`. */
24
+ action: string;
25
+ /** The element, named the way a person would say it, falling back to the ref. */
26
+ target: string;
27
+ /** The engine's verdict for this step, when the step declared anything. */
28
+ verified: string | undefined;
29
+ /** The one sentence naming the deciding evidence. */
30
+ because: string | undefined;
31
+ /**
32
+ * What the drive CLAIMED would happen, in the words it declared before acting.
33
+ *
34
+ * Carried because "3 action(s) FAILED" is not triageable without it. A failure is either a defect
35
+ * in the app or a wrong guess by the driver, those need opposite responses, and the declared
36
+ * consequence is the only thing that tells them apart. Shown on failures only: on a pass it is
37
+ * noise, and the report is read every drive.
38
+ */
39
+ claimed: string | undefined;
40
+ }
41
+ /**
42
+ * Replays this drive ran, and what each came back with.
43
+ *
44
+ * The verdict is `status`, not `passed`. Reading the wrong key reported ten replays as "undecided"
45
+ * on a run where every one of them had answered — which is the same false-nothing this report was
46
+ * written to stop, made by the report itself.
47
+ *
48
+ * Three outcomes, kept apart because they mean different things to whoever reads this. `ok` is the
49
+ * journey still holding. `error` is it failing. `drift` is the flow's own anchors no longer
50
+ * resolving — the app moved under a recording, which is a finding about the RECORDING rather than
51
+ * proof the feature broke, and collapsing it into "failed" would send somebody to fix working code.
52
+ */
53
+ export declare const ReplayOutcome: {
54
+ readonly OK: "ok";
55
+ readonly DRIFT: "drift";
56
+ readonly ERROR: "error";
57
+ };
58
+ export declare function replayedFlows(toolCalls: readonly ToolOutcome[]): {
59
+ name: string;
60
+ status: string | undefined;
61
+ }[];
62
+ /** Reduce the raw call log to the actions that actually drove the app. */
63
+ export declare function drivenSteps(toolCalls: readonly ToolOutcome[]): DrivenStep[];
64
+ /**
65
+ * The account, as lines.
66
+ *
67
+ * Proved and not-proved are counted separately and both are stated, because the failure mode this
68
+ * whole product exists to prevent is a reader taking "we did twelve things" for "twelve things
69
+ * work". `unknown` is deliberately grouped under NOT proved and labelled as undecided evidence
70
+ * rather than as a failure — it calls for a better check, not a code change, and collapsing the two
71
+ * would send an agent to rewrite working code.
72
+ */
73
+ export declare function describeDrive(toolCalls: readonly ToolOutcome[], savedFlows: readonly string[]): string;