@reticlehq/server 3.2.0 → 3.4.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 (437) hide show
  1. package/LICENSE-ENTERPRISE +1 -1
  2. package/README.md +3 -3
  3. package/SKILL.md +23 -22
  4. package/dist/chromium-launch-options.d.ts +4 -3
  5. package/dist/chromium-launch-options.js +6 -2
  6. package/dist/command/cli/adhoc-suite.d.ts +41 -0
  7. package/dist/command/cli/adhoc-suite.js +159 -0
  8. package/dist/command/cli/adhoc-verdict.d.ts +58 -2
  9. package/dist/command/cli/adhoc-verdict.js +108 -13
  10. package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
  11. package/dist/command/cli/answers/gate-hook-message.js +6 -0
  12. package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
  13. package/dist/command/cli/auth/cloud-keystore.js +0 -3
  14. package/dist/command/cli/auth/cloud-session.d.ts +4 -0
  15. package/dist/command/cli/auth/cloud-session.js +2 -0
  16. package/dist/command/cli/cli-doctor.js +30 -8
  17. package/dist/command/cli/cli-flow-commands.d.ts +3 -6
  18. package/dist/command/cli/cli-flow-commands.js +67 -4
  19. package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
  20. package/dist/command/cli/cli-parse-grammar.js +7 -0
  21. package/dist/command/cli/cli-parse-verify.d.ts +2 -5
  22. package/dist/command/cli/cli-parse-verify.js +86 -13
  23. package/dist/command/cli/cli-parse.d.ts +14 -1
  24. package/dist/command/cli/cli-parse.js +54 -8
  25. package/dist/command/cli/cli-update-commands.js +8 -1
  26. package/dist/command/cli/cli-verify.d.ts +80 -0
  27. package/dist/command/cli/cli-verify.js +181 -26
  28. package/dist/command/cli/cloud-cli.js +5 -2
  29. package/dist/command/cli/cloud-kit.d.ts +0 -2
  30. package/dist/command/cli/cloud-kit.js +3 -5
  31. package/dist/command/cli/cloud-login.js +21 -3
  32. package/dist/command/cli/demo/demo-page.js +6 -3
  33. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
  34. package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
  35. package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
  36. package/dist/command/cli/launch/cli-launch.d.ts +9 -29
  37. package/dist/command/cli/launch/cli-launch.js +27 -49
  38. package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
  39. package/dist/command/cli/launch/sdk-version-match.js +206 -0
  40. package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
  41. package/dist/command/cli/ports/resolve/cli-port.d.ts +38 -0
  42. package/dist/command/cli/ports/resolve/cli-port.js +169 -15
  43. package/dist/command/cli/report-command.d.ts +16 -0
  44. package/dist/command/cli/report-command.js +97 -0
  45. package/dist/command/cli/setup-mcp-cli.js +13 -13
  46. package/dist/command/cli/tutorial.d.ts +2 -2
  47. package/dist/command/cli/tutorial.js +21 -7
  48. package/dist/command/cli.js +67 -10
  49. package/dist/command/daemon/binding/daemon-status-probe.d.ts +44 -2
  50. package/dist/command/daemon/binding/daemon-status-probe.js +79 -6
  51. package/dist/command/daemon/binding/port-presence.d.ts +1 -1
  52. package/dist/command/daemon/daemon-resolve.d.ts +9 -0
  53. package/dist/command/daemon/daemon-resolve.js +1 -1
  54. package/dist/command/daemon/idle-shutdown.d.ts +2 -1
  55. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
  56. package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
  57. package/dist/command/dev/stale-issue-guard.d.ts +3 -4
  58. package/dist/command/dev/stale-issue-guard.js +8 -8
  59. package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
  60. package/dist/command/setup/bringup/connect-lease.js +38 -0
  61. package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
  62. package/dist/command/setup/bringup/dev-server-files.js +131 -0
  63. package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
  64. package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
  65. package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
  66. package/dist/command/setup/bringup/ensure-daemon.js +135 -16
  67. package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
  68. package/dist/command/setup/bringup/static-page-server.js +151 -0
  69. package/dist/command/setup/desktop-shape.d.ts +4 -0
  70. package/dist/command/setup/desktop-shape.js +18 -2
  71. package/dist/command/setup/init/init-host.js +8 -0
  72. package/dist/command/setup/init/init-port.d.ts +15 -0
  73. package/dist/command/setup/init/init-port.js +44 -0
  74. package/dist/command/setup/init/init-runtime.d.ts +1 -1
  75. package/dist/command/setup/init/init-runtime.js +39 -2
  76. package/dist/command/setup/node-effects.d.ts +27 -14
  77. package/dist/command/setup/node-effects.js +80 -44
  78. package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
  79. package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
  80. package/dist/command/setup/probe/page-probe.d.ts +13 -2
  81. package/dist/command/setup/probe/page-probe.js +18 -3
  82. package/dist/command/setup/run-setup.d.ts +26 -1
  83. package/dist/command/setup/run-setup.js +96 -13
  84. package/dist/command/setup/session-pick.d.ts +13 -0
  85. package/dist/command/setup/session-pick.js +20 -0
  86. package/dist/command/setup/setup-command.d.ts +2 -14
  87. package/dist/command/setup/setup-command.js +105 -31
  88. package/dist/command/setup/setup-mcp.js +3 -3
  89. package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
  90. package/dist/command/setup/terminal/wait-progress.js +17 -0
  91. package/dist/features/crawl/crawl-empty.js +1 -1
  92. package/dist/features/crawl/crawl-tools.d.ts +6 -0
  93. package/dist/features/crawl/crawl-tools.js +57 -0
  94. package/dist/features/crawl/crawl.d.ts +2 -0
  95. package/dist/features/crawl/crawl.js +171 -156
  96. package/dist/features/exhaust/app-coverage.d.ts +33 -0
  97. package/dist/features/exhaust/app-coverage.js +31 -0
  98. package/dist/features/exhaust/code-coverage.d.ts +47 -0
  99. package/dist/features/exhaust/code-coverage.js +77 -0
  100. package/dist/features/exhaust/exhaust-run.d.ts +33 -0
  101. package/dist/features/exhaust/exhaust-run.js +82 -0
  102. package/dist/features/exhaust/explorer.d.ts +103 -0
  103. package/dist/features/exhaust/explorer.js +195 -0
  104. package/dist/features/exhaust/failure-branches.d.ts +33 -0
  105. package/dist/features/exhaust/failure-branches.js +92 -0
  106. package/dist/features/exhaust/ledger.d.ts +202 -0
  107. package/dist/features/exhaust/ledger.js +252 -0
  108. package/dist/features/exhaust/session-fold.d.ts +31 -0
  109. package/dist/features/exhaust/session-fold.js +79 -0
  110. package/dist/features/exhaust/session-port.d.ts +36 -0
  111. package/dist/features/exhaust/session-port.js +102 -0
  112. package/dist/features/harness/drive-report.d.ts +0 -9
  113. package/dist/features/harness/drive-report.js +18 -0
  114. package/dist/features/harness/driver.js +4 -7
  115. package/dist/features/harness/fill-values.d.ts +6 -0
  116. package/dist/features/harness/fill-values.js +3 -0
  117. package/dist/features/harness/jev-driver.js +36 -22
  118. package/dist/features/harness/openai-driver.js +4 -7
  119. package/dist/features/harness/platform-config.d.ts +10 -0
  120. package/dist/features/harness/platform-config.js +13 -6
  121. package/dist/index.d.ts +10 -1
  122. package/dist/index.js +78 -104
  123. package/dist/judgement/capsule/capsule-store.d.ts +31 -2
  124. package/dist/judgement/capsule/capsule-store.js +58 -6
  125. package/dist/judgement/capsule/capsule.js +5 -0
  126. package/dist/judgement/capsule/causal-summary.d.ts +13 -1
  127. package/dist/judgement/capsule/causal-summary.js +7 -0
  128. package/dist/judgement/domain/domain-model.js +40 -9
  129. package/dist/judgement/oracles/self-instrument.js +4 -2
  130. package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
  131. package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
  132. package/dist/judgement/runs/artifact/gap-report.js +46 -0
  133. package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
  134. package/dist/judgement/runs/artifact/gap-summary.js +85 -0
  135. package/dist/judgement/runs/artifact/render-report.js +4 -1
  136. package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
  137. package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
  138. package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
  139. package/dist/judgement/runs/artifact/to-artifact.js +1 -1
  140. package/dist/judgement/runs/context-tools.js +21 -3
  141. package/dist/judgement/runs/drive-run.js +7 -2
  142. package/dist/judgement/runs/verification-sync.d.ts +3 -1
  143. package/dist/judgement/runs/verification-sync.js +1 -1
  144. package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
  145. package/dist/language/flows/annotate-notes/annotate.js +66 -18
  146. package/dist/language/flows/change/affected.d.ts +9 -0
  147. package/dist/language/flows/change/affected.js +35 -0
  148. package/dist/language/flows/change/gate.d.ts +20 -0
  149. package/dist/language/flows/change/gate.js +12 -1
  150. package/dist/language/flows/flow-anchor.d.ts +12 -0
  151. package/dist/language/flows/flow-anchor.js +20 -0
  152. package/dist/language/flows/flow-author.d.ts +7 -0
  153. package/dist/language/flows/flow-author.js +19 -0
  154. package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
  155. package/dist/language/flows/flow-expect-grammar.js +92 -11
  156. package/dist/language/flows/flow-intent.js +1 -1
  157. package/dist/language/flows/flow-journey.d.ts +54 -0
  158. package/dist/language/flows/flow-journey.js +119 -0
  159. package/dist/language/flows/flow-learning.d.ts +3 -0
  160. package/dist/language/flows/flow-learning.js +39 -8
  161. package/dist/language/flows/flow-replay-run.d.ts +33 -12
  162. package/dist/language/flows/flow-replay-run.js +131 -52
  163. package/dist/language/flows/flow-replay-types.d.ts +9 -0
  164. package/dist/language/flows/flow-replay.d.ts +27 -12
  165. package/dist/language/flows/flow-replay.js +127 -111
  166. package/dist/language/flows/flow-result.d.ts +2 -0
  167. package/dist/language/flows/flow-result.js +25 -0
  168. package/dist/language/flows/flow-step-runners.d.ts +26 -10
  169. package/dist/language/flows/flow-step-runners.js +20 -4
  170. package/dist/language/flows/flow-success.d.ts +21 -6
  171. package/dist/language/flows/flow-success.js +50 -138
  172. package/dist/language/flows/flow-tools.js +17 -5
  173. package/dist/language/flows/flows.d.ts +18 -5
  174. package/dist/language/flows/flows.js +101 -16
  175. package/dist/language/flows/heal.d.ts +9 -1
  176. package/dist/language/flows/heal.js +24 -2
  177. package/dist/language/flows/learned-sources.d.ts +21 -0
  178. package/dist/language/flows/learned-sources.js +84 -0
  179. package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
  180. package/dist/language/flows/recording/recording-backtrack.js +1 -1
  181. package/dist/language/flows/recording/replay-halt.d.ts +2 -1
  182. package/dist/language/flows/recording/replay-halt.js +3 -1
  183. package/dist/language/flows/recording/tape/recordings.d.ts +31 -2
  184. package/dist/language/flows/recording/tape/recordings.js +55 -1
  185. package/dist/language/flows/replay.d.ts +19 -0
  186. package/dist/language/flows/replay.js +53 -13
  187. package/dist/language/flows/stores/annotation-store.d.ts +5 -5
  188. package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
  189. package/dist/language/flows/stores/flake-store.js +2 -3
  190. package/dist/language/flows/suite/coverage.d.ts +1 -1
  191. package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
  192. package/dist/language/flows/verify-change-tools.js +19 -2
  193. package/dist/launch-chromium.d.ts +80 -0
  194. package/dist/launch-chromium.js +218 -0
  195. package/dist/memory/cloud/cloud-config.d.ts +11 -2
  196. package/dist/memory/cloud/cloud-config.js +47 -7
  197. package/dist/memory/cloud/cloud-sync.d.ts +4 -3
  198. package/dist/memory/cloud/cloud-sync.js +7 -10
  199. package/dist/memory/cloud/harness-config.d.ts +2 -0
  200. package/dist/memory/cloud/harness-offer.js +5 -6
  201. package/dist/memory/cloud/harness-switch.js +5 -6
  202. package/dist/memory/cloud/sync-cycle.d.ts +78 -1
  203. package/dist/memory/cloud/sync-cycle.js +321 -53
  204. package/dist/memory/cloud/sync-daemon.d.ts +13 -0
  205. package/dist/memory/cloud/sync-daemon.js +45 -20
  206. package/dist/memory/cloud/sync-disk.js +27 -8
  207. package/dist/memory/impact/impact-store.js +6 -0
  208. package/dist/memory/intent/inline-intent.js +8 -3
  209. package/dist/memory/intent/intent-shard-store.d.ts +33 -47
  210. package/dist/memory/intent/intent-shard-store.js +252 -140
  211. package/dist/memory/intent/intent-shard.d.ts +0 -23
  212. package/dist/memory/intent/intent-shard.js +1 -11
  213. package/dist/memory/intent/intent-store.d.ts +13 -8
  214. package/dist/memory/intent/intent-store.js +46 -90
  215. package/dist/memory/intent/intent-tools.js +34 -23
  216. package/dist/memory/journal/ambient-store.js +2 -3
  217. package/dist/memory/journal/deviation-report.js +4 -1
  218. package/dist/memory/journal/deviation-service.js +10 -2
  219. package/dist/memory/journal/drive-flow.d.ts +19 -14
  220. package/dist/memory/journal/drive-flow.js +65 -48
  221. package/dist/memory/journal/envelope-store.js +2 -3
  222. package/dist/memory/journal/journal-recorder.d.ts +21 -0
  223. package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
  224. package/dist/memory/journal/on-disk/retain-policy.js +44 -0
  225. package/dist/memory/journal/on-disk/retention.d.ts +14 -10
  226. package/dist/memory/journal/on-disk/retention.js +13 -12
  227. package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
  228. package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
  229. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -11
  230. package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -45
  231. package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
  232. package/dist/memory/journal/on-disk/workspace-tiers.js +90 -0
  233. package/dist/memory/journal/route-template.d.ts +22 -0
  234. package/dist/memory/journal/route-template.js +44 -0
  235. package/dist/memory/journal/session-end.d.ts +13 -2
  236. package/dist/memory/journal/session-end.js +88 -18
  237. package/dist/memory/journal/session-journal.d.ts +8 -1
  238. package/dist/memory/journal/session-journal.js +10 -0
  239. package/dist/memory/project/baselines.d.ts +1 -1
  240. package/dist/memory/project/dir/reticle-dir.d.ts +3 -1
  241. package/dist/memory/project/dir/reticle-dir.js +1 -0
  242. package/dist/memory/project/fs/fs-port.js +44 -5
  243. package/dist/memory/project/fs/write-atomic.d.ts +26 -0
  244. package/dist/memory/project/fs/write-atomic.js +48 -0
  245. package/dist/memory/project/project-tools.js +3 -1
  246. package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
  247. package/dist/memory/recall/prior/connection-memory.js +8 -0
  248. package/dist/memory/recall/registered-projects.d.ts +2 -1
  249. package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
  250. package/dist/portal/bridge/auth-failure-reason.js +9 -4
  251. package/dist/portal/bridge/bridge.d.ts +8 -0
  252. package/dist/portal/bridge/bridge.js +43 -2
  253. package/dist/portal/input/js-coverage.d.ts +25 -0
  254. package/dist/portal/input/js-coverage.js +30 -0
  255. package/dist/portal/input/real-input.d.ts +11 -0
  256. package/dist/portal/input/real-input.js +31 -5
  257. package/dist/portal/pool/browser-pool.d.ts +10 -0
  258. package/dist/portal/pool/browser-pool.js +10 -0
  259. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  260. package/dist/portal/pool/playwright-launcher.js +21 -13
  261. package/dist/portal/pool/pool-contract.d.ts +2 -0
  262. package/dist/portal/pool/storage-seed.d.ts +2 -1
  263. package/dist/portal/pool/zero-install.d.ts +27 -0
  264. package/dist/portal/pool/zero-install.js +54 -0
  265. package/dist/portal/realm/web-realm.js +6 -0
  266. package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
  267. package/dist/portal/session/dev-server/dev-command.js +30 -12
  268. package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
  269. package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
  270. package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
  271. package/dist/portal/session/dev-server/route-status-probe.js +57 -0
  272. package/dist/portal/session/dev-server/served-document.d.ts +21 -0
  273. package/dist/portal/session/dev-server/served-document.js +57 -0
  274. package/dist/portal/session/facts/background-traffic.d.ts +9 -0
  275. package/dist/portal/session/facts/background-traffic.js +25 -0
  276. package/dist/portal/session/facts/observed-state.d.ts +11 -0
  277. package/dist/portal/session/facts/observed-state.js +34 -0
  278. package/dist/portal/session/live-control-tools.js +48 -3
  279. package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
  280. package/dist/portal/session/no-session-diagnosis.js +62 -5
  281. package/dist/portal/session/no-session-next-action.d.ts +13 -0
  282. package/dist/portal/session/no-session-next-action.js +48 -9
  283. package/dist/portal/session/no-session-watch.d.ts +18 -0
  284. package/dist/portal/session/no-session-watch.js +88 -2
  285. package/dist/portal/session/presence/attachment-history.d.ts +13 -2
  286. package/dist/portal/session/session-health.d.ts +61 -0
  287. package/dist/portal/session/session-health.js +43 -3
  288. package/dist/portal/session/session-info.d.ts +6 -0
  289. package/dist/portal/session/session-manager.d.ts +29 -6
  290. package/dist/portal/session/session-manager.js +42 -5
  291. package/dist/portal/session/session-reaper.d.ts +28 -0
  292. package/dist/portal/session/session-reaper.js +49 -1
  293. package/dist/portal/session/session-successor.d.ts +11 -0
  294. package/dist/portal/session/session-successor.js +14 -1
  295. package/dist/portal/session/session-verdict-facts.d.ts +28 -0
  296. package/dist/portal/session/session-verdict-facts.js +15 -0
  297. package/dist/portal/session/session.d.ts +10 -0
  298. package/dist/portal/session/session.js +34 -26
  299. package/dist/surface/http-server.d.ts +2 -1
  300. package/dist/surface/http-server.js +4 -2
  301. package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
  302. package/dist/surface/mcp/mcp-proxy.js +5 -5
  303. package/dist/surface/mcp/mcp.js +4 -2
  304. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
  305. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
  306. package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
  307. package/dist/surface/mcp/server-instructions.js +21 -8
  308. package/dist/surface/tools/act/after-match.d.ts +17 -0
  309. package/dist/surface/tools/act/after-match.js +30 -0
  310. package/dist/surface/tools/act/already-true.d.ts +40 -0
  311. package/dist/surface/tools/act/already-true.js +36 -0
  312. package/dist/surface/tools/act/durable.d.ts +69 -0
  313. package/dist/surface/tools/act/durable.js +139 -0
  314. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +19 -6
  315. package/dist/surface/tools/act/navigation/navigate-arrival.js +60 -10
  316. package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
  317. package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
  318. package/dist/surface/tools/act/proved-controls.d.ts +9 -0
  319. package/dist/surface/tools/act/proved-controls.js +11 -0
  320. package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
  321. package/dist/surface/tools/act/sequence-grade.js +32 -1
  322. package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
  323. package/dist/surface/tools/act/settle-in-flight.js +29 -6
  324. package/dist/surface/tools/act-capsule.d.ts +7 -0
  325. package/dist/surface/tools/act-capsule.js +68 -8
  326. package/dist/surface/tools/act-sequence-tool.js +25 -8
  327. package/dist/surface/tools/act-tools.js +51 -74
  328. package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
  329. package/dist/surface/tools/args/numeric-bounds.js +30 -3
  330. package/dist/surface/tools/assert/assert-grade.js +26 -3
  331. package/dist/surface/tools/assert/assert-verdict.js +32 -6
  332. package/dist/surface/tools/browser-tools.js +17 -4
  333. package/dist/surface/tools/coverage-tools.js +27 -1
  334. package/dist/surface/tools/dynamic-tools.js +6 -0
  335. package/dist/surface/tools/error-recovery.js +4 -4
  336. package/dist/surface/tools/feedback-invite.js +6 -4
  337. package/dist/surface/tools/feedback-tools.d.ts +2 -1
  338. package/dist/surface/tools/feedback-tools.js +1 -1
  339. package/dist/surface/tools/harness-explore.d.ts +3 -2
  340. package/dist/surface/tools/harness-explore.js +7 -6
  341. package/dist/surface/tools/invoke-tool.js +17 -4
  342. package/dist/surface/tools/lease-hint.d.ts +8 -0
  343. package/dist/surface/tools/lease-hint.js +10 -0
  344. package/dist/surface/tools/lease-tools.d.ts +16 -28
  345. package/dist/surface/tools/lease-tools.js +68 -11
  346. package/dist/surface/tools/lineage-tools.js +1 -1
  347. package/dist/surface/tools/live-call-text.js +4 -1
  348. package/dist/surface/tools/observe-tools.js +23 -7
  349. package/dist/surface/tools/real-input-attempt.d.ts +0 -5
  350. package/dist/surface/tools/real-input-attempt.js +1 -1
  351. package/dist/surface/tools/tool-kit.d.ts +5 -0
  352. package/dist/surface/tools/tool-kit.js +6 -1
  353. package/dist/surface/tools/tool-surface.js +17 -5
  354. package/dist/surface/tools/tools.js +68 -29
  355. package/dist/telemetry/argument-shape.d.ts +0 -2
  356. package/dist/telemetry/argument-shape.js +0 -4
  357. package/dist/telemetry/error-class.d.ts +2 -3
  358. package/dist/telemetry/error-class.js +2 -3
  359. package/dist/telemetry/feedback-local.d.ts +1 -0
  360. package/dist/telemetry/feedback-local.js +1 -3
  361. package/dist/telemetry/feedback.d.ts +1 -1
  362. package/dist/telemetry/harness-drive.d.ts +0 -2
  363. package/dist/telemetry/harness-drive.js +0 -4
  364. package/dist/telemetry/hud-metrics.d.ts +32 -0
  365. package/dist/telemetry/hud-metrics.js +95 -0
  366. package/dist/telemetry/session-metrics.d.ts +5 -0
  367. package/dist/telemetry/session-metrics.js +15 -1
  368. package/dist/wire-journal.d.ts +34 -0
  369. package/dist/wire-journal.js +104 -0
  370. package/dist/workspace-packages.d.ts +0 -2
  371. package/dist/workspace-packages.js +1 -9
  372. package/docs/README.md +5 -2
  373. package/docs/actions.mdx +3 -3
  374. package/docs/agent-cheatsheet.md +4 -4
  375. package/docs/architecture.md +6 -6
  376. package/docs/best-practices.mdx +1 -1
  377. package/docs/capabilities.mdx +32 -1
  378. package/docs/cli/cloud.mdx +21 -9
  379. package/docs/cli/doctor.mdx +4 -0
  380. package/docs/cli/feedback.mdx +2 -2
  381. package/docs/cli/gate.mdx +9 -4
  382. package/docs/cli/init.mdx +1 -0
  383. package/docs/cli/report.mdx +44 -0
  384. package/docs/cli/update.mdx +10 -0
  385. package/docs/cli.mdx +9 -0
  386. package/docs/desktop.mdx +14 -10
  387. package/docs/environment.mdx +87 -0
  388. package/docs/faq.mdx +18 -16
  389. package/docs/first-drive.md +5 -13
  390. package/docs/flows.md +32 -2
  391. package/docs/frameworks.mdx +140 -9
  392. package/docs/getting-started.md +3 -3
  393. package/docs/http-transport.md +6 -3
  394. package/docs/human-control.md +2 -2
  395. package/docs/index.mdx +16 -6
  396. package/docs/install-agentic.mdx +37 -1
  397. package/docs/install-manual.mdx +1 -1
  398. package/docs/integration-patterns.md +3 -3
  399. package/docs/multi-agent-testing.md +1 -1
  400. package/docs/packages/babel-plugin.mdx +1 -1
  401. package/docs/packages/browser.mdx +2 -2
  402. package/docs/packages/react.mdx +2 -2
  403. package/docs/packages/tauri.mdx +1 -1
  404. package/docs/packages/vite-plugin.mdx +2 -2
  405. package/docs/packages.mdx +9 -9
  406. package/docs/predicates.mdx +176 -7
  407. package/docs/quickstart.mdx +2 -2
  408. package/docs/recipes.mdx +2 -2
  409. package/docs/state-management.mdx +2 -2
  410. package/docs/system-map.md +1 -1
  411. package/docs/telemetry.md +1 -1
  412. package/docs/token-efficiency.md +3 -3
  413. package/docs/tools/act-and-wait.mdx +1 -0
  414. package/docs/tools/act-sequence.mdx +1 -1
  415. package/docs/tools/console.mdx +1 -1
  416. package/docs/tools/inspect.mdx +1 -1
  417. package/docs/tools/network.mdx +1 -1
  418. package/docs/tools/query.mdx +11 -11
  419. package/docs/tools/sessions.mdx +1 -1
  420. package/docs/tools/snapshot.mdx +1 -1
  421. package/docs/tools/state.mdx +1 -1
  422. package/docs/tools/wait-for.mdx +1 -1
  423. package/docs/usage.md +8 -7
  424. package/docs/use-cases.mdx +157 -0
  425. package/docs/vs-screenshots.mdx +3 -3
  426. package/docs/what-is-recorded.md +62 -0
  427. package/docs/what-is-reticle.mdx +17 -20
  428. package/docs/why-reticle.mdx +14 -21
  429. package/package.json +11 -10
  430. package/dist/judgement/capsule/minimize.d.ts +0 -12
  431. package/dist/judgement/capsule/minimize.js +0 -22
  432. package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
  433. package/dist/judgement/outcome/predicate-to-expect.js +0 -148
  434. package/dist/judgement/phenomena/phenomena.d.ts +0 -26
  435. package/dist/judgement/phenomena/phenomena.js +0 -101
  436. package/dist/language/flows/flow-report.d.ts +0 -22
  437. package/dist/language/flows/flow-report.js +0 -106
@@ -69,6 +69,19 @@ interface NextActionFacts {
69
69
  * action that cannot help and can overwrite it.
70
70
  */
71
71
  authRefused?: boolean;
72
+ /**
73
+ * The bridge refused the last hello for a non-token reason (protocol or wire-contract skew), in
74
+ * the bridge's own `cause — fix` sentence. Current, like `authRefused`: nothing connected since.
75
+ * Every other action below would send the agent to start, open or re-wire an app that is running,
76
+ * instrumented and being turned away.
77
+ */
78
+ helloRefused?: string;
79
+ /**
80
+ * The url the session that went away was on, from the daemon's own tombstone. Positive evidence
81
+ * about where THIS project's app lives, which the port scan is not: the scan is machine-wide, and
82
+ * its one hit is as likely to be another repo's dev server as this one's.
83
+ */
84
+ lastKnownUrl?: string;
72
85
  }
73
86
  export declare function nextActionFor(facts: NextActionFacts): NoSessionNextAction;
74
87
  /** The same action as one sentence, for the prose message the human reads. */
@@ -12,7 +12,29 @@
12
12
  *
13
13
  * Pure. The facts arrive from the watch; nothing here touches the disk or the clock.
14
14
  */
15
- import { NoSessionAction, ReticleEnv } from '@reticlehq/core';
15
+ import { NoSessionAction, RETICLE_URL_PARAM, ReticleEnv } from '@reticlehq/core';
16
+ /**
17
+ * The departed session's url as a human would type it: the params a Reticle launcher stamps on a
18
+ * lease are stripped, because reopening WITH them would adopt the dead lease's identity. Undefined
19
+ * for a url that does not parse, or one with no port to name (a desktop webview's opaque origin).
20
+ */
21
+ function reopenableUrl(raw) {
22
+ if (raw === undefined)
23
+ return undefined;
24
+ let url;
25
+ try {
26
+ url = new URL(raw);
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ if ('http:' !== url.protocol && 'https:' !== url.protocol)
32
+ return undefined;
33
+ for (const param of Object.values(RETICLE_URL_PARAM))
34
+ url.searchParams.delete(param);
35
+ const port = '' === url.port ? ('https:' === url.protocol ? 443 : 80) : Number(url.port);
36
+ return { url: url.toString(), port };
37
+ }
16
38
  /** `reticle init`, the only command here that is Reticle's own and so cannot be wrong. */
17
39
  const INIT_COMMAND = 'reticle init';
18
40
  const OPEN_COMMAND = 'reticle open';
@@ -28,6 +50,13 @@ export function nextActionFor(facts) {
28
50
  if (splitBrain !== undefined) {
29
51
  return { action: NoSessionAction.DAEMON_SPLIT, reason: splitBrain };
30
52
  }
53
+ if (facts.helloRefused !== undefined) {
54
+ return {
55
+ action: NoSessionAction.OPEN_APP,
56
+ reason: `a page dialled this daemon and was refused (${facts.helloRefused}); do that, then reload ` +
57
+ 'the page — the SDK does not retry after a refusal.',
58
+ };
59
+ }
31
60
  // `authRefused` is checked here and not only further down, because this branch short-circuits.
32
61
  //
33
62
  // Both refusal branches below were already written and already right, and neither could be
@@ -44,16 +73,24 @@ export function nextActionFor(facts) {
44
73
  if (facts.everConnected && true !== facts.authRefused) {
45
74
  const listening = facts.listening;
46
75
  const only = 1 === listening.length ? listening[0] : undefined;
47
- const bound = 0 === listening.length
48
- ? ''
76
+ // The departed session's own url first: it is where this project's app demonstrably was, while
77
+ // a lone port from the scan may belong to anybody.
78
+ const known = reopenableUrl(facts.lastKnownUrl);
79
+ const bound = known !== undefined
80
+ ? ` It was last on ${known.url}; reopen that — do not start a second stack.`
81
+ : 0 === listening.length
82
+ ? ''
83
+ : only === undefined
84
+ ? ` An app is already listening on ${listening.join(', ')}; just open the URL the human names — do not start a second stack.`
85
+ : ` An app is already listening on ${String(only)}; just open ${LOCALHOST}:${String(only)} — do not start a second stack.`;
86
+ const target = known !== undefined
87
+ ? { command: `${OPEN_COMMAND} ${known.url}`, port: known.port }
49
88
  : only === undefined
50
- ? ` An app is already listening on ${listening.join(', ')}; just open the URL the human names — do not start a second stack.`
51
- : ` An app is already listening on ${String(only)}; just open ${LOCALHOST}:${String(only)} — do not start a second stack.`;
89
+ ? {}
90
+ : { command: `${OPEN_COMMAND} ${LOCALHOST}:${String(only)}`, port: only };
52
91
  return {
53
92
  action: NoSessionAction.REOPEN_APP,
54
- ...(only === undefined
55
- ? {}
56
- : { command: `${OPEN_COMMAND} ${LOCALHOST}:${String(only)}`, port: only }),
93
+ ...target,
57
94
  reason: 'a session was connected to this daemon earlier, so the wiring is correct — the tab was ' +
58
95
  'closed, reloaded, or the lease aged out. Reopen the app, or take one you own with ' +
59
96
  'reticle_lease {action:"acquire", url}.' +
@@ -108,7 +145,9 @@ export function nextActionFor(facts) {
108
145
  // does not cover, it reported the app was not running.
109
146
  reason: 'nothing is listening on the ports Reticle scans, so the app is probably not running — ' +
110
147
  'though that scan is narrow, so if it IS up on another port, ask for its URL instead of ' +
111
- `starting a second one. This is the project's own \`${dev.script}\` script — run it in the ` +
148
+ `starting a second one. This is the project's own ` +
149
+ (undefined === dev.script ? 'launcher' : `\`${dev.script}\` script`) +
150
+ ` — run it in the ` +
112
151
  'background, tell the human it is running, then call reticle_sessions again.',
113
152
  };
114
153
  }
@@ -45,6 +45,24 @@ interface NoSessionWatchOptions {
45
45
  * Omitted on a daemon with no pool, and auto-attach is then simply off.
46
46
  */
47
47
  attach?: (url: string) => Promise<unknown>;
48
+ /**
49
+ * The ports THIS project's dev servers announced into the registry the build plugins write.
50
+ *
51
+ * Auto-attach opens a port only when it can be attributed to this project. The port scan is
52
+ * machine-wide, so its one hit is as likely to be another repo's app as this one's — and now that
53
+ * the attach path can launch an installed Chrome, a wrong guess parks a hidden headless browser on
54
+ * somebody else's app. Injected for tests; production reads the registry scoped by projectId/root.
55
+ */
56
+ ownDevServerPorts?: () => readonly number[];
57
+ /**
58
+ * What the route the last session was on answers right now, as an HTTP status, or undefined
59
+ * when there is no answer to report.
60
+ *
61
+ * Injected so tests can pin the observation without opening a socket. Production (no override)
62
+ * asks the route itself — see route-status-probe. The one fact that separates a route that 500s
63
+ * from a closed tab (#808, option 3).
64
+ */
65
+ routeStatus?: (url: string) => Promise<number | undefined>;
48
66
  /**
49
67
  * Where the durable "an app has connected here before" bit lives. The daemon's state home unless
50
68
  * a test says otherwise.
@@ -9,13 +9,14 @@
9
9
  * It only probes while NOTHING is connected. Once a session is live the question is moot, and a
10
10
  * daemon that outlives the agent by hours has no business scanning ports it does not need.
11
11
  */
12
+ import { probeRouteStatus } from './dev-server/route-status-probe.js';
12
13
  import { probeDevServers, probeDevServerStates } from './dev-server/dev-server-probe.js';
13
14
  import { homedir } from 'node:os';
14
15
  import { registeredElsewhere } from '../../memory/recall/registered-projects.js';
15
16
  import { explainNoSession } from './no-session-diagnosis.js';
16
17
  import { detectDevCommand } from './dev-server/dev-command.js';
17
18
  import { nextActionFor, renderNextAction } from './no-session-next-action.js';
18
- import { readProjectFramework, readProjectId, readProjectPort, } from '../../command/cli/ports/resolve/cli-port.js';
19
+ import { readProjectFramework, readProjectId, readProjectIsDesktop, readProjectPort, } from '../../command/cli/ports/resolve/cli-port.js';
19
20
  import { discoverProjectConfigs } from '../../command/cli/config/config-discovery.js';
20
21
  import { hasProjectConnectedBefore, rememberConnected, } from '../../memory/recall/prior/connection-memory.js';
21
22
  import { isAlive, reticleStateHome } from '../../command/daemon/daemon.js';
@@ -24,6 +25,8 @@ import { stallUptime } from './stall-clock.js';
24
25
  import { probeDaemon } from '../../surface/mcp/mcp-proxy.js';
25
26
  import { findOccupiedSiblings } from '../../command/cli/ports/sibling-ports.js';
26
27
  import { isAuthRefusalReason } from '../bridge/auth-failure-reason.js';
28
+ import { devServersForProject } from '@reticlehq/core';
29
+ import { readDevServers } from '../../command/daemon/dev-servers.js';
27
30
  /** Slow enough to be free, fast enough that a dev server started 15s ago is already reflected. */
28
31
  const REFRESH_MS = 15_000;
29
32
  /** The URL an auto-attach opens. `localhost` for the same reason the probe uses it: both stacks. */
@@ -63,6 +66,15 @@ export function startNoSessionWatch(options) {
63
66
  */
64
67
  let slowListeners = [];
65
68
  let siblingListeners = [];
69
+ /**
70
+ * What the last-known route answered, cached beside the port scan for the same reason the scan
71
+ * is cached: the hint is read synchronously, so every fact it carries has to be gathered before
72
+ * it is asked. Keyed by URL so a status is never attributed to a route other than the one it
73
+ * came from — a fresh departure on a different URL reads as "not asked yet", not as the old
74
+ * answer.
75
+ */
76
+ let lastKnownStatus;
77
+ const routeStatus = options.routeStatus ?? probeRouteStatus;
66
78
  let running = false;
67
79
  /** Ports auto-attach has already spent its one attempt on. Bounded: never a loop, never a retry. */
68
80
  const attempted = new Set();
@@ -155,6 +167,31 @@ export function startNoSessionWatch(options) {
155
167
  * Bounded by construction: one attempt per port per daemon. A failure is recorded, never retried —
156
168
  * a retry loop against a broken Chromium install would spin for the daemon's whole life.
157
169
  */
170
+ const ownDevServerPorts = options.ownDevServerPorts ??
171
+ (() => devServersForProject(readDevServers(stateDir), {
172
+ projectId: readProjectId(directory),
173
+ root: directory,
174
+ }).map((entry) => entry.port));
175
+ /**
176
+ * Is `port` demonstrably this project's app? Either its dev server announced it, or this
177
+ * project's own last session was on it. Anything else is a guess, and a guess that opens a
178
+ * browser is worse than the sentence the diagnosis already writes about the ports it saw.
179
+ */
180
+ const attributable = (port) => {
181
+ if (ownDevServerPorts().includes(port))
182
+ return true;
183
+ const known = options.sessions.lastKnown?.();
184
+ const projectId = readProjectId(directory);
185
+ if (known === undefined || projectId === undefined || known.projectId !== projectId) {
186
+ return false;
187
+ }
188
+ try {
189
+ return Number(new URL(known.url).port) === port;
190
+ }
191
+ catch {
192
+ return false;
193
+ }
194
+ };
158
195
  const autoAttach = async (ports) => {
159
196
  const attach = options.attach;
160
197
  if (attach === undefined)
@@ -166,6 +203,8 @@ export function startNoSessionWatch(options) {
166
203
  return;
167
204
  if (!isWired())
168
205
  return;
206
+ if (!attributable(only))
207
+ return;
169
208
  attempted.add(only);
170
209
  try {
171
210
  await attach(`${LOCALHOST}:${String(only)}`);
@@ -193,6 +232,14 @@ export function startNoSessionWatch(options) {
193
232
  : options.probe === undefined
194
233
  ? await findOccupiedSiblings(options.port, probeDaemon)
195
234
  : [];
235
+ // Asked once per departed URL, on the same background cadence as the port scan, and never
236
+ // on the hint's own path. A url already answered is not asked again; a new departure is.
237
+ const known = options.sessions.lastKnown?.();
238
+ if (known !== undefined && lastKnownStatus?.url !== known.url) {
239
+ const status = await routeStatus(known.url);
240
+ if (status !== undefined)
241
+ lastKnownStatus = { url: known.url, status };
242
+ }
196
243
  await autoAttach(ports);
197
244
  })
198
245
  .catch(() => {
@@ -225,6 +272,21 @@ export function startNoSessionWatch(options) {
225
272
  */
226
273
  const lastCloseWasAuthFailure = () => isAuthRefusalReason(options.sessions.lastClosure?.()?.reason) &&
227
274
  true !== options.sessions.connectedSinceLastClosure?.();
275
+ /**
276
+ * A hello refused for anything but its token, while it is still the bridge's current state.
277
+ *
278
+ * Only closures that carry the refused page count: the bridge attaches one exactly when a HELLO
279
+ * was turned away, so an origin or handshake-pool refusal (which has its own sentence on
280
+ * `resolve`) is not mistaken for one. Same ordering clause as the token refusal above.
281
+ */
282
+ const currentHelloRefusal = () => {
283
+ const closure = options.sessions.lastClosure?.();
284
+ if (closure?.page === undefined || isAuthRefusalReason(closure.reason))
285
+ return undefined;
286
+ if (true === options.sessions.connectedSinceLastClosure?.())
287
+ return undefined;
288
+ return { reason: closure.reason, ...closure.page };
289
+ };
228
290
  const nextAction = (scope) => {
229
291
  const split = splitBrain();
230
292
  return nextActionFor({
@@ -235,8 +297,18 @@ export function startNoSessionWatch(options) {
235
297
  // Read when asked, like every other fact here: a page can dial at any moment, and a daemon
236
298
  // that cached "nothing has been refused" at boot would keep saying so.
237
299
  authRefused: lastCloseWasAuthFailure(),
300
+ ...(() => {
301
+ const refused = currentHelloRefusal();
302
+ return refused === undefined ? {} : { helloRefused: refused.reason };
303
+ })(),
238
304
  ...(split === undefined ? {} : { splitBrain: split }),
239
305
  listening,
306
+ // The url the departed tab was on — the same tombstone the prose diagnosis quotes, so the
307
+ // command and the sentence beside it name the same page.
308
+ ...(() => {
309
+ const known = options.sessions.lastKnown?.();
310
+ return known === undefined ? {} : { lastKnownUrl: known.url };
311
+ })(),
240
312
  // Read when asked, like everything else here: a `package.json` can gain a dev script, and a
241
313
  // daemon that cached "there is none" at boot would keep saying so for the rest of the day.
242
314
  dev: detectDevCommand(directory),
@@ -272,12 +344,19 @@ export function startNoSessionWatch(options) {
272
344
  // beside it still said the tab had been closed. One fact, two answers, and only one of them
273
345
  // was right. Read when asked, like the rest: a page can be refused at any moment.
274
346
  authRefused: lastCloseWasAuthFailure(),
347
+ ...(() => {
348
+ const refused = currentHelloRefusal();
349
+ return refused === undefined ? {} : { helloRefused: refused };
350
+ })(),
275
351
  // Ranks the causes. Read when asked, like the rest: `init` writes this file after the daemon
276
352
  // starts on an ordinary first install.
277
353
  ...(() => {
278
354
  const framework = readProjectFramework(directory);
279
355
  return framework === undefined ? {} : { framework };
280
356
  })(),
357
+ // A desktop app is opened by its own dev command, never by a browser URL or a lease. Read when
358
+ // asked, like the framework: `init` may wire the app after this daemon started.
359
+ ...(readProjectIsDesktop(directory) ? { desktop: true } : {}),
281
360
  // Decided from the session that actually went away, not from a lifetime tally: the lease
282
361
  // sentence is only right when the thing that vanished WAS a lease.
283
362
  leaseExpired: (() => {
@@ -289,7 +368,14 @@ export function startNoSessionWatch(options) {
289
368
  // has none to name.
290
369
  ...(() => {
291
370
  const known = options.sessions.lastKnown?.();
292
- return undefined === known ? {} : { lastKnownUrl: known.url };
371
+ if (undefined === known)
372
+ return {};
373
+ // The status rides only when it was fetched for THIS url. A departure since the fetch
374
+ // means the cached answer is about a route nobody is asking about.
375
+ const status = lastKnownStatus !== undefined && lastKnownStatus.url === known.url
376
+ ? { lastKnownStatus: lastKnownStatus.status }
377
+ : {};
378
+ return { lastKnownUrl: known.url, ...status };
293
379
  })(),
294
380
  // How long this daemon has been waiting with no app. The diagnosis uses it to surface
295
381
  // "install never finished" — the same condition telemetry already knows about.
@@ -15,11 +15,22 @@
15
15
  * The clock is injected, per the repo rule: this is arithmetic over timestamps, and a test that
16
16
  * needs to wait four seconds to check a four-second gap is a test nobody runs.
17
17
  */
18
- /** What an agent needs to know about the continuity of one session. */
18
+ /**
19
+ * What an agent needs to know about the continuity of one session.
20
+ *
21
+ * Every field here measures the SDK-to-daemon LINK. None of them measures the app: the origin the
22
+ * page was served from can stop answering entirely without any of these moving, because the socket
23
+ * that reports them is between the daemon and a document the browser is already holding. Reported
24
+ * as a false read of app health in #938, where a tab whose dev server was dead carried `outages: 0`
25
+ * and `connectedSinceMs: 11min` and was taken for a healthy app.
26
+ */
19
27
  interface Attachment {
20
28
  /** How long THIS attachment has lasted. Resets on reconnect — it is not the session's lifetime. */
21
29
  connectedSinceMs: number;
22
- /** How many times this session dropped and came back. Zero means it never left. */
30
+ /**
31
+ * How many times this session dropped and came back. Zero means the LINK never dropped; it does
32
+ * not mean the app behind it is alive.
33
+ */
23
34
  outages: number;
24
35
  /** The most recent gap, when there has been one. */
25
36
  lastOutage?: {
@@ -38,6 +38,22 @@ export interface SessionHealth {
38
38
  * on `reticle_sessions` or `reticle_lease` is not on the surface it reads every call.
39
39
  */
40
40
  versionSkew?: string;
41
+ /**
42
+ * The session's event ledger, once it has grown enough to be worth a reader's attention.
43
+ *
44
+ * Present only past `LEDGER_NOTICE_FRACTION` of the cap, so a healthy session costs nothing --
45
+ * the same rule `pendingNavigationMs` follows. A ledger below that is doing its job quietly.
46
+ *
47
+ * It is here because nothing reported it. A repeating uncaught error wrote one session's ledger
48
+ * until the disk was full; neither the daemon nor `doctor` nor any
49
+ * health counter said a word, and the user found out when the disk was full (#986). A cap bounds
50
+ * the damage but does not make the writing visible, and this block is the surface an agent
51
+ * already reads on every act and assert.
52
+ */
53
+ ledger?: {
54
+ bytes: number;
55
+ capBytes: number;
56
+ };
41
57
  }
42
58
  /**
43
59
  * How long a request may be in flight before it is worth reporting on session health.
@@ -84,6 +100,18 @@ interface BufferEnvelope {
84
100
  * so a clean/empty result there is trustworthy and costs zero tokens.
85
101
  */
86
102
  export declare function bufferEnvelope(session: HealthSubject): BufferEnvelope;
103
+ /**
104
+ * The session registered under `session.id` NOW, for describing a tab after a call that may have
105
+ * replaced its document. A reload or full navigation registers a new Session under the same id, and
106
+ * the object a handler resolved first is the page that unloaded; it reported itself hidden on the way
107
+ * out, so its health said "throttled" about a tab that was fine. Optional-called because test doubles
108
+ * are partial registries.
109
+ */
110
+ export declare function currentOf<S extends {
111
+ id: string;
112
+ }>(registry: {
113
+ get?: (id: string) => S | undefined;
114
+ }, session: S): S;
87
115
  /** The `session` (and optional throttled `warning`) block spliced onto act/assert results. */
88
116
  interface HealthEnvelope {
89
117
  session?: SessionHealth;
@@ -142,4 +170,37 @@ interface HealthReport {
142
170
  * keep its previous value instead of silently flipping a session to "visible" on a partial report.
143
171
  */
144
172
  export declare function readHealthEvent(data: Record<string, unknown>): HealthReport;
173
+ /**
174
+ * The ledger block for the health payload, or nothing when it is not worth saying.
175
+ *
176
+ * Absent below `LEDGER_NOTICE_FRACTION` of the cap, and absent when the size has not been measured
177
+ * yet -- a reader must be able to tell "not measured" from "empty", and a zero reported before the
178
+ * first append would be a claim rather than a reading.
179
+ */
180
+ export declare function ledgerNotice(ledger: {
181
+ bytes: number;
182
+ capBytes: number;
183
+ } | undefined): {
184
+ ledger?: {
185
+ bytes: number;
186
+ capBytes: number;
187
+ };
188
+ };
189
+ /**
190
+ * Every health field that is present only when it means something.
191
+ *
192
+ * One place answers "which optional facts belong on the health block", so a session does not have
193
+ * to know the rule for each. A healthy session gets an empty object back and its health block
194
+ * carries only the fields that are always true.
195
+ */
196
+ export declare function healthNotices(events: readonly ReticleEvent[], elapsed: number, ledger: {
197
+ bytes: number;
198
+ capBytes: number;
199
+ } | undefined): {
200
+ pendingNavigationMs?: number;
201
+ ledger?: {
202
+ bytes: number;
203
+ capBytes: number;
204
+ };
205
+ };
145
206
  export {};
@@ -1,4 +1,4 @@
1
- import { BrowserBrand, EventType, BUFFER_EVICTION_WARNING, THROTTLED_STARVED_NOTE, THROTTLED_WARNING, } from '@reticlehq/core';
1
+ import { BrowserBrand, EventType, BUFFER_EVICTION_WARNING, THROTTLED_STARVED_NOTE, SESSION_HEALTH, THROTTLED_WARNING, } from '@reticlehq/core';
2
2
  /**
3
3
  * How long a request may be in flight before it is worth reporting on session health.
4
4
  *
@@ -29,7 +29,8 @@ export const PENDING_NAVIGATION_NOTICE_MS = 10_000;
29
29
  */
30
30
  const SKEW_VERDICT_WARNING = 'version skew: `dispatched` and `settled` on this session mean the event was sent, NOT that the ' +
31
31
  'app acted on it — a skewed pair drops actions silently. Re-read the control (aria-checked, ' +
32
- 'data-state, its text) before trusting any verdict from this session, and converge the versions:';
32
+ 'data-state, its text) before trusting any verdict from this session, and converge the versions ' +
33
+ '(see session.versionSkew).';
33
34
  /**
34
35
  * The age of the oldest request that STARTED and never completed, or undefined when there is none
35
36
  * worth reporting.
@@ -68,6 +69,16 @@ export function bufferEnvelope(session) {
68
69
  return {};
69
70
  return { buffer: { held: total, dropped, note: BUFFER_EVICTION_WARNING } };
70
71
  }
72
+ /**
73
+ * The session registered under `session.id` NOW, for describing a tab after a call that may have
74
+ * replaced its document. A reload or full navigation registers a new Session under the same id, and
75
+ * the object a handler resolved first is the page that unloaded; it reported itself hidden on the way
76
+ * out, so its health said "throttled" about a tab that was fine. Optional-called because test doubles
77
+ * are partial registries.
78
+ */
79
+ export function currentOf(registry, session) {
80
+ return registry.get?.(session.id) ?? session;
81
+ }
71
82
  /**
72
83
  * Build the health envelope for a tool result. When the session is **nominal** (focused, not
73
84
  * throttled, no escape-hatch recommendation) the block is OMITTED entirely — a healthy session
@@ -90,7 +101,7 @@ export function healthEnvelope(session) {
90
101
  // a skewed link makes the ACTION unreliable, and there is no point warning about the quality of an
91
102
  // observation of something that may never have happened.
92
103
  if (health.versionSkew !== undefined) {
93
- return { session: health, warning: `${SKEW_VERDICT_WARNING} ${health.versionSkew}` };
104
+ return { session: health, warning: SKEW_VERDICT_WARNING };
94
105
  }
95
106
  return health.throttled ? { session: health, warning: THROTTLED_WARNING } : { session: health };
96
107
  }
@@ -152,3 +163,32 @@ export function readHealthEvent(data) {
152
163
  brand: Object.values(BrowserBrand).find((known) => known === data['brand']),
153
164
  };
154
165
  }
166
+ /**
167
+ * The ledger block for the health payload, or nothing when it is not worth saying.
168
+ *
169
+ * Absent below `LEDGER_NOTICE_FRACTION` of the cap, and absent when the size has not been measured
170
+ * yet -- a reader must be able to tell "not measured" from "empty", and a zero reported before the
171
+ * first append would be a claim rather than a reading.
172
+ */
173
+ export function ledgerNotice(ledger) {
174
+ if (ledger === undefined || 0 >= ledger.capBytes)
175
+ return {};
176
+ const fraction = ledger.bytes / ledger.capBytes;
177
+ return fraction < SESSION_HEALTH.LEDGER_NOTICE_FRACTION ? {} : { ledger };
178
+ }
179
+ /**
180
+ * Every health field that is present only when it means something.
181
+ *
182
+ * One place answers "which optional facts belong on the health block", so a session does not have
183
+ * to know the rule for each. A healthy session gets an empty object back and its health block
184
+ * carries only the fields that are always true.
185
+ */
186
+ export function healthNotices(events, elapsed, ledger) {
187
+ // From event t=0, not a cursor: a wedge that began before the current action is exactly the case
188
+ // a per-window reading cannot see, and is the one both reporters hit.
189
+ const stuck = pendingNavigationMs(events, elapsed);
190
+ return {
191
+ ...(stuck === undefined ? {} : { pendingNavigationMs: stuck }),
192
+ ...ledgerNotice(ledger),
193
+ };
194
+ }
@@ -45,6 +45,12 @@ export interface SessionInfo {
45
45
  * A tab that dropped for four seconds and came back is indistinguishable from one that never left
46
46
  * (#117), and that difference decides whether a verdict over the window can be trusted. Attached
47
47
  * by `SessionManager.list()`, which is the layer that sees every connect and disconnect.
48
+ *
49
+ * Scoped to the SDK-to-daemon LINK, and the names do not say so on their own. A zombie tab whose
50
+ * dev server had died reported `outages: 0` and `connectedSinceMs: 11min`, and both were correct:
51
+ * the page's socket was still attached to the daemon. Read as app health, they said the app was
52
+ * up while nothing was listening on the port (#938). The describe() on the `reticle_sessions`
53
+ * output schema carries that caveat, because that is where an agent reads it.
48
54
  */
49
55
  attachment?: {
50
56
  connectedSinceMs: number;
@@ -14,6 +14,17 @@ interface ResolveScope {
14
14
  }
15
15
  /** The scheme://host:port of a URL, or undefined if it can't be parsed. Used to compare origins. */
16
16
  export declare function originOf(url: string | undefined): string | undefined;
17
+ /** What a refused hello said about itself: the page it was on and the project it claimed. */
18
+ export interface RefusedPage {
19
+ readonly url?: string;
20
+ readonly projectId?: string;
21
+ }
22
+ /** One bridge-initiated close, with the page that was turned away when the hello named one. */
23
+ export interface ClosureRecord {
24
+ readonly at: number;
25
+ readonly reason: string;
26
+ readonly page?: RefusedPage;
27
+ }
17
28
  export declare class SessionManager {
18
29
  #private;
19
30
  /** Set the active project's default resolve scope (called once at daemon wiring). */
@@ -22,6 +33,15 @@ export declare class SessionManager {
22
33
  add(session: Session): Session | undefined;
23
34
  remove(session: Session): boolean;
24
35
  get(sessionId: string): Session | undefined;
36
+ /**
37
+ * Did this id belong to a session that has since disconnected?
38
+ *
39
+ * `resolve` answers a departed id with its live SUCCESSOR, which is what keeps a reload working
40
+ * for everything that observes. A caller that is about to do something DESTRUCTIVE needs the
41
+ * other answer: whether the thing it named is still there. Ending a session it did not name is
42
+ * not a recoverable mistake (#983).
43
+ */
44
+ departed(sessionId: string): SessionIdentity | undefined;
25
45
  list(): SessionInfo[];
26
46
  /** Continuity for one session — "has this been attached the whole time I am reasoning about?" */
27
47
  attachmentOf(sessionId: string): ReturnType<AttachmentHistory['of']>;
@@ -48,13 +68,16 @@ export declare class SessionManager {
48
68
  setNoSessionNextAction(next: (() => NoSessionNextAction | undefined) | undefined): void;
49
69
  noSessionNextAction(): NoSessionNextAction | undefined;
50
70
  everConnected(): boolean;
51
- /** Record a bridge-initiated close so `resolve` can explain a session that vanished. */
52
- noteClosure(reason: string, at: number): void;
71
+ /**
72
+ * Record a bridge-initiated close so `resolve` can explain a session that vanished.
73
+ *
74
+ * `page` is what the refused hello said about itself, when it said anything. A refusal is the one
75
+ * piece of positive evidence the no-session diagnosis has, and "a page was refused" without WHICH
76
+ * page leaves the reader unable to tell their app from another project's tab on the same port.
77
+ */
78
+ noteClosure(reason: string, at: number, page?: RefusedPage): void;
53
79
  /** The most recent bridge-initiated close, if any. */
54
- lastClosure(): {
55
- at: number;
56
- reason: string;
57
- } | undefined;
80
+ lastClosure(): ClosureRecord | undefined;
58
81
  connectedSinceLastClosure(): boolean;
59
82
  /**
60
83
  * The id of the session that most recently went away, if any.