@reticlehq/server 2.12.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. package/SKILL.md +67 -109
  2. package/dist/bridge/auth-failure-reason.d.ts +6 -2
  3. package/dist/bridge/auth-failure-reason.js +15 -8
  4. package/dist/bridge/bridge.d.ts +18 -1
  5. package/dist/bridge/bridge.js +247 -164
  6. package/dist/bridge/pairing-token.d.ts +5 -0
  7. package/dist/bridge/pairing-token.js +28 -0
  8. package/dist/bridge/protocol-skew.d.ts +37 -0
  9. package/dist/bridge/protocol-skew.js +45 -0
  10. package/dist/capsule/capsule-store.d.ts +6 -6
  11. package/dist/capsule/causal-summary.js +7 -2
  12. package/dist/cli/cli-doctor.js +31 -1
  13. package/dist/cli/cli-launch.d.ts +11 -1
  14. package/dist/cli/cli-launch.js +59 -10
  15. package/dist/cli/cli-parse.d.ts +15 -1
  16. package/dist/cli/cli-parse.js +212 -8
  17. package/dist/cli/cli-verify.d.ts +1 -0
  18. package/dist/cli/cli-verify.js +3 -2
  19. package/dist/cli/cloud-cli.js +428 -259
  20. package/dist/cli/cloud-keystore.d.ts +37 -0
  21. package/dist/cli/cloud-keystore.js +97 -0
  22. package/dist/cli/cloud-kit.d.ts +37 -0
  23. package/dist/cli/cloud-kit.js +121 -0
  24. package/dist/cli/cloud-login.d.ts +17 -0
  25. package/dist/cli/cloud-login.js +183 -0
  26. package/dist/cli/cloud-session.d.ts +48 -0
  27. package/dist/cli/cloud-session.js +53 -0
  28. package/dist/cli/config-discovery.js +1 -0
  29. package/dist/cli/daemon-start-options.d.ts +32 -0
  30. package/dist/cli/daemon-start-options.js +38 -0
  31. package/dist/cli/device-flow.d.ts +76 -0
  32. package/dist/cli/device-flow.js +56 -0
  33. package/dist/cli/doctor-rows.d.ts +9 -0
  34. package/dist/cli/doctor-rows.js +9 -0
  35. package/dist/cli/drive-attach.d.ts +13 -0
  36. package/dist/cli/drive-attach.js +18 -0
  37. package/dist/cli/drive-command.js +7 -3
  38. package/dist/cli/mcp-command.js +51 -20
  39. package/dist/cli/project-name.d.ts +30 -0
  40. package/dist/cli/project-name.js +44 -0
  41. package/dist/cli/status-next-action.d.ts +14 -0
  42. package/dist/cli/status-next-action.js +4 -4
  43. package/dist/cli.d.ts +1 -0
  44. package/dist/cli.js +134 -80
  45. package/dist/cloud/cloud-config.d.ts +22 -1
  46. package/dist/cloud/cloud-config.js +64 -5
  47. package/dist/cloud/cloud-sync.d.ts +5 -5
  48. package/dist/cloud/cloud-sync.js +33 -2
  49. package/dist/cloud/sync-cycle.d.ts +110 -0
  50. package/dist/cloud/sync-cycle.js +270 -0
  51. package/dist/cloud/sync-daemon.d.ts +69 -0
  52. package/dist/cloud/sync-daemon.js +260 -0
  53. package/dist/cloud/sync-disk.d.ts +9 -0
  54. package/dist/cloud/sync-disk.js +191 -0
  55. package/dist/cloud/sync-hash.d.ts +2 -0
  56. package/dist/cloud/sync-hash.js +35 -0
  57. package/dist/crawl/crawl-tools.js +5 -3
  58. package/dist/crawl/crawl.d.ts +3 -0
  59. package/dist/crawl/crawl.js +5 -0
  60. package/dist/daemon/daemon-resolve.d.ts +43 -0
  61. package/dist/daemon/daemon-resolve.js +69 -0
  62. package/dist/daemon/dev-servers.d.ts +15 -0
  63. package/dist/daemon/dev-servers.js +68 -0
  64. package/dist/daemon/wake-decision.d.ts +32 -0
  65. package/dist/daemon/wake-decision.js +38 -0
  66. package/dist/domain/domain-tools.js +2 -2
  67. package/dist/events/contradictions.d.ts +18 -0
  68. package/dist/events/contradictions.js +134 -21
  69. package/dist/events/declared.d.ts +7 -4
  70. package/dist/events/declared.js +15 -6
  71. package/dist/events/echo-mismatch.js +64 -14
  72. package/dist/events/event-filters.d.ts +8 -0
  73. package/dist/events/event-filters.js +19 -2
  74. package/dist/events/predicate-eval.js +53 -5
  75. package/dist/events/predicate-precheck.d.ts +8 -0
  76. package/dist/events/predicate-precheck.js +41 -0
  77. package/dist/events/predicate-route.d.ts +14 -0
  78. package/dist/events/predicate-route.js +36 -8
  79. package/dist/events/predicate.d.ts +5 -0
  80. package/dist/events/predicate.js +37 -5
  81. package/dist/events/split-text-miss.d.ts +30 -0
  82. package/dist/events/split-text-miss.js +44 -0
  83. package/dist/flows/flake.d.ts +6 -6
  84. package/dist/flows/flow-memory-consult.d.ts +55 -0
  85. package/dist/flows/flow-memory-consult.js +38 -0
  86. package/dist/flows/flow-replay-run.d.ts +18 -7
  87. package/dist/flows/flow-replay-run.js +239 -22
  88. package/dist/flows/flow-replay.js +11 -2
  89. package/dist/flows/flow-step-runners.d.ts +8 -0
  90. package/dist/flows/flow-step-runners.js +8 -2
  91. package/dist/flows/flow-store-for-session.d.ts +31 -0
  92. package/dist/flows/flow-store-for-session.js +33 -0
  93. package/dist/flows/flow-tools.js +58 -32
  94. package/dist/flows/flows.d.ts +8 -0
  95. package/dist/flows/flows.js +468 -2
  96. package/dist/flows/heal-run.js +5 -2
  97. package/dist/flows/predicate-to-expect.d.ts +17 -12
  98. package/dist/flows/predicate-to-expect.js +30 -12
  99. package/dist/flows/recordings.d.ts +13 -1
  100. package/dist/flows/recordings.js +17 -2
  101. package/dist/flows/replay.d.ts +22 -2
  102. package/dist/flows/replay.js +28 -2
  103. package/dist/flows/verify-change-tools.js +5 -2
  104. package/dist/honesty/blind-spots.d.ts +55 -2
  105. package/dist/honesty/blind-spots.js +63 -3
  106. package/dist/honesty/honesty.d.ts +24 -0
  107. package/dist/honesty/honesty.js +25 -1
  108. package/dist/honesty/instrumentation-gaps.d.ts +9 -0
  109. package/dist/honesty/instrumentation-gaps.js +29 -2
  110. package/dist/honesty/verified.d.ts +2 -1
  111. package/dist/honesty/verified.js +16 -1
  112. package/dist/http-server.js +12 -0
  113. package/dist/impact/impact-recorder.d.ts +31 -8
  114. package/dist/impact/impact-recorder.js +104 -21
  115. package/dist/impact/impact-store.d.ts +15 -1
  116. package/dist/impact/impact-store.js +43 -2
  117. package/dist/index.js +113 -7
  118. package/dist/init/agent-rules.d.ts +13 -6
  119. package/dist/init/agent-rules.js +23 -12
  120. package/dist/init/astro-patch.d.ts +16 -0
  121. package/dist/init/astro-patch.js +28 -0
  122. package/dist/init/capabilities.d.ts +4 -1
  123. package/dist/init/capabilities.js +22 -2
  124. package/dist/init/confirm.d.ts +31 -7
  125. package/dist/init/confirm.js +48 -8
  126. package/dist/init/cra.d.ts +20 -4
  127. package/dist/init/cra.js +57 -17
  128. package/dist/init/csp-check.js +17 -2
  129. package/dist/init/csp-doctor.d.ts +8 -1
  130. package/dist/init/csp-doctor.js +8 -3
  131. package/dist/init/dev-script.d.ts +3 -1
  132. package/dist/init/dev-script.js +8 -5
  133. package/dist/init/format-generated.d.ts +18 -0
  134. package/dist/init/format-generated.js +39 -0
  135. package/dist/init/install-hint.d.ts +9 -0
  136. package/dist/init/install-hint.js +29 -0
  137. package/dist/init/mcp-clients.d.ts +7 -0
  138. package/dist/init/mcp-clients.js +7 -0
  139. package/dist/init/mcp.d.ts +1 -1
  140. package/dist/init/mcp.js +1 -1
  141. package/dist/init/node-io.js +11 -1
  142. package/dist/init/plan-framework.js +81 -45
  143. package/dist/init/plan.d.ts +20 -7
  144. package/dist/init/plan.js +20 -45
  145. package/dist/init/preflight.d.ts +28 -0
  146. package/dist/init/preflight.js +38 -0
  147. package/dist/init/run.d.ts +32 -9
  148. package/dist/init/run.js +141 -119
  149. package/dist/init/slash-command.d.ts +1 -1
  150. package/dist/init/snippets.js +41 -25
  151. package/dist/init/vite-config.d.ts +1 -1
  152. package/dist/init/vite-config.js +39 -25
  153. package/dist/init/workspace-apps.d.ts +17 -10
  154. package/dist/init/workspace-apps.js +110 -0
  155. package/dist/input/network-mock-tools.js +3 -6
  156. package/dist/input/real-input.js +25 -5
  157. package/dist/input/scroll-tools.js +4 -6
  158. package/dist/input/viewport-tools.js +5 -4
  159. package/dist/intent/inline-intent.d.ts +10 -19
  160. package/dist/intent/inline-intent.js +91 -1
  161. package/dist/intent/intent-shard-store.d.ts +72 -0
  162. package/dist/intent/intent-shard-store.js +203 -0
  163. package/dist/intent/intent-shard.d.ts +411 -0
  164. package/dist/intent/intent-shard.js +176 -0
  165. package/dist/intent/intent-store.d.ts +12 -0
  166. package/dist/intent/intent-store.js +21 -0
  167. package/dist/intent/intent-subject.d.ts +45 -0
  168. package/dist/intent/intent-subject.js +177 -0
  169. package/dist/intent/intent-tools.js +65 -2
  170. package/dist/intent/open-intents.d.ts +21 -0
  171. package/dist/intent/open-intents.js +15 -0
  172. package/dist/license/license-env.d.ts +29 -0
  173. package/dist/license/license-env.js +148 -0
  174. package/dist/loopback-agent.d.ts +31 -0
  175. package/dist/loopback-agent.js +57 -0
  176. package/dist/mcp/attach-memory.d.ts +18 -2
  177. package/dist/mcp/attach-memory.js +50 -7
  178. package/dist/mcp/client-identity.d.ts +31 -0
  179. package/dist/mcp/client-identity.js +51 -0
  180. package/dist/mcp/mcp-post-transport.d.ts +26 -0
  181. package/dist/mcp/mcp-post-transport.js +109 -0
  182. package/dist/mcp/mcp-proxy.d.ts +2 -1
  183. package/dist/mcp/mcp-proxy.js +52 -62
  184. package/dist/mcp/mcp.js +7 -1
  185. package/dist/mcp/proxy-backoff.d.ts +11 -0
  186. package/dist/mcp/proxy-backoff.js +13 -0
  187. package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
  188. package/dist/mcp/proxy-daemon-probe.js +7 -1
  189. package/dist/mcp/proxy-handshake.d.ts +23 -0
  190. package/dist/mcp/proxy-handshake.js +31 -0
  191. package/dist/mcp/tool-catalog-cache.d.ts +8 -0
  192. package/dist/mcp/tool-catalog-cache.js +12 -0
  193. package/dist/memory/memory-tools.d.ts +2 -0
  194. package/dist/memory/memory-tools.js +82 -0
  195. package/dist/memory/project-memory.d.ts +34 -0
  196. package/dist/memory/project-memory.js +96 -0
  197. package/dist/oracles/propose-consequences.js +12 -3
  198. package/dist/pool/browser-pool.d.ts +63 -4
  199. package/dist/pool/browser-pool.js +113 -4
  200. package/dist/pool/playwright-launcher.js +9 -0
  201. package/dist/project/project-tools.js +3 -3
  202. package/dist/project/reticle-dir.d.ts +15 -2
  203. package/dist/project/reticle-dir.js +21 -4
  204. package/dist/runs/run-store.d.ts +10 -0
  205. package/dist/runs/run-store.js +10 -0
  206. package/dist/runs/verification-sync.d.ts +0 -11
  207. package/dist/runs/verification-sync.js +22 -1
  208. package/dist/runs/verify-server.d.ts +8 -1
  209. package/dist/runs/verify-server.js +14 -2
  210. package/dist/session/command-timeout.d.ts +5 -2
  211. package/dist/session/command-timeout.js +5 -4
  212. package/dist/session/lease-visibility.d.ts +67 -0
  213. package/dist/session/lease-visibility.js +76 -0
  214. package/dist/session/no-session-diagnosis.d.ts +23 -0
  215. package/dist/session/no-session-diagnosis.js +58 -17
  216. package/dist/session/no-session-next-action.d.ts +7 -0
  217. package/dist/session/no-session-next-action.js +42 -3
  218. package/dist/session/no-session-watch.d.ts +10 -6
  219. package/dist/session/no-session-watch.js +51 -19
  220. package/dist/session/pending-commands.d.ts +11 -1
  221. package/dist/session/pending-commands.js +12 -2
  222. package/dist/session/resolve-within.d.ts +34 -0
  223. package/dist/session/resolve-within.js +53 -0
  224. package/dist/session/session-info.d.ts +18 -0
  225. package/dist/session/session-info.js +11 -0
  226. package/dist/session/session-manager.d.ts +14 -0
  227. package/dist/session/session-manager.js +61 -3
  228. package/dist/session/session-mirror.d.ts +16 -0
  229. package/dist/session/session-mirror.js +22 -0
  230. package/dist/session/session-recommendation.d.ts +19 -5
  231. package/dist/session/session-recommendation.js +25 -9
  232. package/dist/session/session-successor.js +36 -1
  233. package/dist/session/session-tools.js +2 -2
  234. package/dist/session/session.d.ts +22 -2
  235. package/dist/session/session.js +87 -7
  236. package/dist/session/wall-clock.d.ts +12 -0
  237. package/dist/session/wall-clock.js +14 -0
  238. package/dist/setup/agent-configs.d.ts +96 -0
  239. package/dist/setup/agent-configs.js +333 -0
  240. package/dist/setup/agent-io.d.ts +9 -0
  241. package/dist/setup/agent-io.js +16 -0
  242. package/dist/setup/agent-writer.d.ts +51 -0
  243. package/dist/setup/agent-writer.js +115 -0
  244. package/dist/setup/approval-migration.d.ts +47 -0
  245. package/dist/setup/approval-migration.js +68 -0
  246. package/dist/setup/auto-approve.d.ts +85 -0
  247. package/dist/setup/auto-approve.js +150 -0
  248. package/dist/setup/bridge-port.d.ts +15 -0
  249. package/dist/setup/bridge-port.js +24 -0
  250. package/dist/setup/desktop-shape.d.ts +57 -0
  251. package/dist/setup/desktop-shape.js +63 -0
  252. package/dist/setup/dev-server-wait.d.ts +60 -0
  253. package/dist/setup/dev-server-wait.js +65 -0
  254. package/dist/setup/drive-agent.d.ts +71 -0
  255. package/dist/setup/drive-agent.js +171 -0
  256. package/dist/setup/drive-plan.d.ts +68 -0
  257. package/dist/setup/drive-plan.js +130 -0
  258. package/dist/setup/ensure-daemon.d.ts +29 -0
  259. package/dist/setup/ensure-daemon.js +58 -0
  260. package/dist/setup/init-runtime.d.ts +47 -0
  261. package/dist/setup/init-runtime.js +160 -0
  262. package/dist/setup/interrupt.d.ts +25 -0
  263. package/dist/setup/interrupt.js +40 -0
  264. package/dist/setup/license-key.d.ts +39 -0
  265. package/dist/setup/license-key.js +72 -0
  266. package/dist/setup/listeners.d.ts +37 -0
  267. package/dist/setup/listeners.js +85 -0
  268. package/dist/setup/node-effects.d.ts +60 -0
  269. package/dist/setup/node-effects.js +210 -0
  270. package/dist/setup/page-probe.d.ts +39 -0
  271. package/dist/setup/page-probe.js +49 -0
  272. package/dist/setup/relaunch.d.ts +36 -0
  273. package/dist/setup/relaunch.js +57 -0
  274. package/dist/setup/remaining-steps.d.ts +19 -0
  275. package/dist/setup/remaining-steps.js +31 -0
  276. package/dist/setup/run-setup.d.ts +99 -0
  277. package/dist/setup/run-setup.js +187 -0
  278. package/dist/setup/session-pick.d.ts +47 -0
  279. package/dist/setup/session-pick.js +51 -0
  280. package/dist/setup/setup-command.d.ts +45 -0
  281. package/dist/setup/setup-command.js +199 -0
  282. package/dist/setup/setup-options.d.ts +74 -0
  283. package/dist/setup/setup-options.js +67 -0
  284. package/dist/setup/transcripts.d.ts +16 -0
  285. package/dist/setup/transcripts.js +43 -0
  286. package/dist/status-payload.d.ts +14 -1
  287. package/dist/status-payload.js +23 -1
  288. package/dist/telemetry/daemon-telemetry.d.ts +8 -1
  289. package/dist/telemetry/daemon-telemetry.js +52 -5
  290. package/dist/telemetry/error-class.js +1 -1
  291. package/dist/telemetry/feedback-context.d.ts +9 -1
  292. package/dist/telemetry/feedback-context.js +60 -29
  293. package/dist/telemetry/feedback-local.d.ts +15 -0
  294. package/dist/telemetry/feedback-local.js +56 -0
  295. package/dist/telemetry/feedback.d.ts +11 -0
  296. package/dist/telemetry/feedback.js +19 -6
  297. package/dist/telemetry/init-telemetry.d.ts +2 -0
  298. package/dist/telemetry/init-telemetry.js +2 -0
  299. package/dist/telemetry/install-source.d.ts +14 -0
  300. package/dist/telemetry/install-source.js +30 -0
  301. package/dist/telemetry/license-activation.d.ts +7 -1
  302. package/dist/telemetry/license-activation.js +9 -5
  303. package/dist/telemetry/mcp-connection.js +7 -0
  304. package/dist/telemetry/project-profile.d.ts +12 -1
  305. package/dist/telemetry/project-profile.js +11 -6
  306. package/dist/telemetry/proxy-telemetry.d.ts +3 -0
  307. package/dist/telemetry/proxy-telemetry.js +21 -0
  308. package/dist/telemetry/session-metrics.d.ts +12 -0
  309. package/dist/telemetry/session-metrics.js +35 -1
  310. package/dist/telemetry/telemetry.js +17 -2
  311. package/dist/telemetry/tool-refused.d.ts +3 -1
  312. package/dist/telemetry/tool-refused.js +34 -2
  313. package/dist/tools/act-capsule.d.ts +10 -0
  314. package/dist/tools/act-capsule.js +8 -2
  315. package/dist/tools/act-danger.d.ts +2 -1
  316. package/dist/tools/act-danger.js +11 -3
  317. package/dist/tools/act-observation.d.ts +41 -0
  318. package/dist/tools/act-observation.js +36 -0
  319. package/dist/tools/act-preflight.d.ts +20 -0
  320. package/dist/tools/act-preflight.js +98 -0
  321. package/dist/tools/act-sequence-retry.d.ts +45 -0
  322. package/dist/tools/act-sequence-retry.js +58 -0
  323. package/dist/tools/act-sequence-tool.d.ts +13 -0
  324. package/dist/tools/act-sequence-tool.js +135 -0
  325. package/dist/tools/act-target.d.ts +14 -0
  326. package/dist/tools/act-target.js +47 -0
  327. package/dist/tools/act-tools.d.ts +21 -1
  328. package/dist/tools/act-tools.js +100 -213
  329. package/dist/tools/assert-verdict.js +22 -16
  330. package/dist/tools/browser-tools.js +6 -2
  331. package/dist/tools/dynamic-tools.js +24 -5
  332. package/dist/tools/error-recovery.d.ts +1 -0
  333. package/dist/tools/error-recovery.js +8 -0
  334. package/dist/tools/invoke-tool.js +59 -9
  335. package/dist/tools/lease-tools.d.ts +30 -4
  336. package/dist/tools/lease-tools.js +169 -5
  337. package/dist/tools/merged-name-redirect.d.ts +13 -0
  338. package/dist/tools/merged-name-redirect.js +22 -0
  339. package/dist/tools/numeric-bounds.d.ts +100 -0
  340. package/dist/tools/numeric-bounds.js +125 -0
  341. package/dist/tools/observe-tools.js +47 -40
  342. package/dist/tools/read-tools.js +28 -7
  343. package/dist/tools/real-input-attempt.d.ts +10 -0
  344. package/dist/tools/real-input-attempt.js +59 -1
  345. package/dist/tools/reconcile-tools.js +4 -4
  346. package/dist/tools/tool-kit.d.ts +12 -0
  347. package/dist/tools/tool-kit.js +6 -1
  348. package/dist/tools/tool-names.d.ts +2 -0
  349. package/dist/tools/tool-names.js +2 -0
  350. package/dist/tools/tools-helpers.d.ts +19 -0
  351. package/dist/tools/tools-helpers.js +46 -0
  352. package/dist/tools/tools.d.ts +2 -0
  353. package/dist/tools/tools.js +56 -8
  354. package/dist/update/reticle-deps.d.ts +9 -0
  355. package/dist/update/reticle-deps.js +27 -0
  356. package/dist/update/sdk-sync.d.ts +1 -2
  357. package/dist/update/sdk-sync.js +1 -21
  358. package/dist/update/update-nudge.d.ts +18 -0
  359. package/dist/update/update-nudge.js +20 -0
  360. package/dist/version/peer-announce.js +4 -2
  361. package/dist/version/sdk-fix.d.ts +47 -0
  362. package/dist/version/sdk-fix.js +127 -0
  363. package/dist/version/version-nudge.d.ts +14 -0
  364. package/dist/version/version-nudge.js +21 -0
  365. package/dist/version/version-skew.d.ts +38 -3
  366. package/dist/version/version-skew.js +83 -7
  367. package/dist/visual/visual-store.d.ts +6 -6
  368. package/dist/visual/visual-store.js +18 -16
  369. package/dist/visual/visual-tools.js +57 -12
  370. package/docs/cli/doctor.mdx +1 -0
  371. package/docs/cli/serve.mdx +4 -2
  372. package/docs/cli.mdx +7 -3
  373. package/docs/desktop.mdx +1 -1
  374. package/docs/docs.json +1 -1
  375. package/docs/getting-started.md +19 -3
  376. package/docs/index.mdx +9 -6
  377. package/docs/install-agentic.mdx +38 -12
  378. package/docs/install-manual.mdx +1 -1
  379. package/docs/packages.mdx +1 -1
  380. package/docs/skill-file.mdx +3 -3
  381. package/docs/telemetry-contract.md +108 -4
  382. package/docs/tools-act-sequence.mdx +16 -0
  383. package/docs/usage.md +1 -1
  384. package/package.json +4 -3
  385. package/dist/init/cursor.d.ts +0 -24
  386. package/dist/init/cursor.js +0 -80
@@ -0,0 +1,41 @@
1
+ import { residualQueryChecks } from './predicate-schema.js';
2
+ /** Composite predicates nest; the unusable one can be at any depth. */
3
+ const NESTED_KEYS = ['predicates', 'predicate'];
4
+ const isRecord = (v) => 'object' === typeof v && null !== v;
5
+ /** Every element query in a predicate tree, in the order a reader would meet them. */
6
+ function elementQueries(predicate, found = []) {
7
+ if (!isRecord(predicate))
8
+ return found;
9
+ if (isRecord(predicate['query']))
10
+ found.push(predicate['query']);
11
+ for (const key of NESTED_KEYS) {
12
+ const nested = predicate[key];
13
+ if (Array.isArray(nested))
14
+ for (const child of nested)
15
+ elementQueries(child, found);
16
+ else if (isRecord(nested))
17
+ elementQueries(nested, found);
18
+ }
19
+ return found;
20
+ }
21
+ /**
22
+ * The reason this predicate cannot be evaluated, or undefined when it can.
23
+ *
24
+ * Only checks what is knowable WITHOUT the page — a locator that names fields the resolver drops is
25
+ * wrong whatever the DOM contains, which is what makes it safe to refuse before acting. Anything
26
+ * that depends on what is actually rendered is still decided after the action, where it belongs.
27
+ */
28
+ export function unevaluablePredicateReason(predicate) {
29
+ for (const query of elementQueries(predicate)) {
30
+ const residual = residualQueryChecks(query);
31
+ if (0 === residual.unusable.length)
32
+ continue;
33
+ return (`the element locator ignores ${residual.unusable.map((f) => `\`${f}\``).join(', ')} ` +
34
+ `in ${JSON.stringify(query)} — it resolves by the first of by+value, component/source, role, ` +
35
+ 'text, label, placeholder, testid, alt that is present, and nothing here can check the rest. ' +
36
+ 'Assert them one locator at a time, or move the extra field into the locator. ' +
37
+ 'Nothing was acted on: this predicate could never have been evaluated, so refusing it costs ' +
38
+ 'you nothing and spending the action on it would have cost you the verdict.');
39
+ }
40
+ return undefined;
41
+ }
@@ -17,6 +17,20 @@ export declare const RouteDecidedBy: {
17
17
  readonly CHANGE: "route-change";
18
18
  readonly CURRENT: "current-route";
19
19
  };
20
+ /**
21
+ * The path the router is actually on.
22
+ *
23
+ * Under a hash router the document pathname is `/` on every page and the whole route lives in the
24
+ * fragment — which is why `contains` below matches the full route. `pathname` was left comparing the
25
+ * document path, so `{ pathname: '/posts/12/show' }` could never pass on a HashRouter app, the
26
+ * standard router for a packaged Electron/Tauri renderer. That is a guaranteed false red: Reticle
27
+ * telling a user their working app is broken.
28
+ *
29
+ * The fragment WINS rather than being accepted alongside the document path. Honouring both would
30
+ * make `{ pathname: '/' }` trivially true on every hash-routed page — a green that says nothing
31
+ * about where the app is, which is the failure this predicate exists to prevent.
32
+ */
33
+ export declare function routePathOf(pathname: string, hash: string): string;
20
34
  export declare function evalRoute(events: ReticleEvent[], p: Extract<Predicate, {
21
35
  kind: typeof PredicateKind.ROUTE;
22
36
  }>,
@@ -17,6 +17,26 @@ export const RouteDecidedBy = {
17
17
  CHANGE: 'route-change',
18
18
  CURRENT: 'current-route',
19
19
  };
20
+ /**
21
+ * The path the router is actually on.
22
+ *
23
+ * Under a hash router the document pathname is `/` on every page and the whole route lives in the
24
+ * fragment — which is why `contains` below matches the full route. `pathname` was left comparing the
25
+ * document path, so `{ pathname: '/posts/12/show' }` could never pass on a HashRouter app, the
26
+ * standard router for a packaged Electron/Tauri renderer. That is a guaranteed false red: Reticle
27
+ * telling a user their working app is broken.
28
+ *
29
+ * The fragment WINS rather than being accepted alongside the document path. Honouring both would
30
+ * make `{ pathname: '/' }` trivially true on every hash-routed page — a green that says nothing
31
+ * about where the app is, which is the failure this predicate exists to prevent.
32
+ */
33
+ export function routePathOf(pathname, hash) {
34
+ if (!hash.startsWith('#/'))
35
+ return pathname;
36
+ const withoutHash = hash.slice(1);
37
+ const query = withoutHash.indexOf('?');
38
+ return -1 === query ? withoutHash : withoutHash.slice(0, query);
39
+ }
20
40
  /**
21
41
  * The session's live URL, split the way a route-change event already carries it.
22
42
  *
@@ -30,6 +50,7 @@ function readCurrentRoute(url) {
30
50
  const hash = parsed?.hash ?? '';
31
51
  return {
32
52
  pathname,
53
+ routePath: routePathOf(pathname, hash),
33
54
  full: `${pathname}${search}${hash}`,
34
55
  decidedBy: RouteDecidedBy.CURRENT,
35
56
  data: { pathname, search, hash, url, decidedBy: RouteDecidedBy.CURRENT },
@@ -55,12 +76,17 @@ currentUrl) {
55
76
  const routes = events.filter((e) => e.type === EventType.ROUTE_CHANGE);
56
77
  const last = routes.at(-1);
57
78
  const reading = last !== undefined
58
- ? {
59
- pathname: str(last.data['pathname']) ?? str(last.data['to']) ?? '',
60
- full: `${str(last.data['pathname']) ?? str(last.data['to']) ?? ''}${str(last.data['search']) ?? ''}${str(last.data['hash']) ?? ''}`,
61
- decidedBy: RouteDecidedBy.CHANGE,
62
- data: { ...last.data, decidedBy: RouteDecidedBy.CHANGE },
63
- }
79
+ ? (() => {
80
+ const changedPath = str(last.data['pathname']) ?? str(last.data['to']) ?? '';
81
+ const changedHash = str(last.data['hash']) ?? '';
82
+ return {
83
+ pathname: changedPath,
84
+ routePath: routePathOf(changedPath, changedHash),
85
+ full: `${changedPath}${str(last.data['search']) ?? ''}${changedHash}`,
86
+ decidedBy: RouteDecidedBy.CHANGE,
87
+ data: { ...last.data, decidedBy: RouteDecidedBy.CHANGE },
88
+ };
89
+ })()
64
90
  : currentUrl === undefined || 0 === currentUrl.length
65
91
  ? undefined
66
92
  : readCurrentRoute(currentUrl);
@@ -73,11 +99,13 @@ currentUrl) {
73
99
  assertion: 'route.changed',
74
100
  };
75
101
  }
76
- const pathname = reading.pathname;
102
+ // The ROUTER's path, not the document's — see routePathOf.
103
+ const pathname = reading.routePath;
77
104
  if (p.pathname !== undefined && pathname !== p.pathname) {
78
105
  return {
79
106
  pass: false,
80
- failureReason: `${describeRouteSource(reading)} is '${pathname}', expected '${p.pathname}'`,
107
+ // No ` is` after the clause: it read "route changed to is '/'".
108
+ failureReason: `${describeRouteSource(reading)} '${pathname}', expected '${p.pathname}'`,
81
109
  observed: `${describeRouteSource(reading)} '${pathname}'`,
82
110
  expected: `route '${p.pathname}'`,
83
111
  assertion: 'route.pathname',
@@ -45,6 +45,11 @@ export interface PredicateSession {
45
45
  * this hook (e.g. tests that never disconnect) simply leaves in-flight predicates until timeout.
46
46
  */
47
47
  onDisconnect?(listener: () => void): () => void;
48
+ /**
49
+ * True when the tab is hidden or stale enough that the browser is throttling it.
50
+ * Optional: a fake that never throttles simply omits it.
51
+ */
52
+ throttled?(): boolean;
48
53
  }
49
54
  export declare function evaluatePredicate(session: PredicateSession, predicate: Predicate, since?: number, diagnose?: boolean): Promise<EvalResult>;
50
55
  /**
@@ -1,8 +1,9 @@
1
- import { ElementState, PredicateKind, ReticleCommand, isSameDocument, } from '@reticlehq/core';
1
+ import { ElementState, PredicateKind, ReticleCommand, THROTTLED_STARVED_NOTE, isSameDocument, } from '@reticlehq/core';
2
2
  import { log } from '../log.js';
3
3
  import { bindSpanContext } from '../trace.js';
4
4
  import { selectPath, capDepth } from '../session/state-select.js';
5
5
  import { describeTestidMiss } from './testid-near-miss.js';
6
+ import { describeSplitTextMiss } from './split-text-miss.js';
6
7
  import { predicateToExpectedLinks } from '../capsule/predicate-to-links.js';
7
8
  import { isAmbient, ambientKeyOf } from '../journal/ambient.js';
8
9
  import { evalRoute } from './predicate-route.js';
@@ -154,7 +155,12 @@ async function evalElement(session, query, state, absent, diagnose) {
154
155
  // testid-near-miss.ts.
155
156
  const present = match.hint?.presentTestids ?? [];
156
157
  const alsoHere = query.testid === undefined ? undefined : describeTestidMiss(query.testid, present);
157
- const suffix = alsoHere === undefined || '' === alsoHere ? '' : ` — ${alsoHere}`;
158
+ // A text miss where the string is on the page but split across children reads exactly like an
159
+ // element that never rendered. Naming the container is the difference between a retry and a bug
160
+ // report against working code. See split-text-miss.ts.
161
+ const splitText = describeSplitTextMiss(match.hint?.splitText);
162
+ const clause = splitText ?? (alsoHere === undefined || '' === alsoHere ? undefined : alsoHere);
163
+ const suffix = clause === undefined ? '' : ` — ${clause}`;
158
164
  return {
159
165
  pass: false,
160
166
  failureReason: `no element matched ${subject}${state === undefined ? '' : ` in state '${state}'`}${suffix}`,
@@ -419,7 +425,33 @@ const SUPERSEDED_ASSERTION = 'window.evidence-superseded';
419
425
  function predicateSince(predicate) {
420
426
  return 'since' in predicate && 'number' === typeof predicate.since ? predicate.since : 0;
421
427
  }
422
- export async function evaluatePredicate(session, predicate, since = 0,
428
+ /**
429
+ * A miss on a throttled tab is not a missing render. The browser has starved the tab, so a timeout
430
+ * there may mean it never ran — which must not look like "the text is absent".
431
+ *
432
+ * Sets `inconclusive` only. The PROSE is already handled one layer up by
433
+ * `annotateStarvedFailure` (session-health.ts), which suffixes the same fact onto the
434
+ * failureReason so the concrete diagnosis still leads; writing it here as well would put the
435
+ * sentence in every throttled failure twice. What was missing was never the sentence — it was the
436
+ * FIELD an agent gates on, so a starved wait graded `assertion-failed` and sent somebody to fix
437
+ * working code.
438
+ *
439
+ * Idempotent, and a more specific `inconclusive` (unreadable locator, superseded window) is never
440
+ * overwritten.
441
+ */
442
+ function annotateThrottledMiss(session, result) {
443
+ if (result.pass)
444
+ return result;
445
+ if (true !== session.throttled?.())
446
+ return result;
447
+ if (result.inconclusive !== undefined)
448
+ return result;
449
+ return { ...result, inconclusive: THROTTLED_STARVED_NOTE };
450
+ }
451
+ export async function evaluatePredicate(session, predicate, since = 0, diagnose = true) {
452
+ return annotateThrottledMiss(session, await evaluatePredicateRaw(session, predicate, since, diagnose));
453
+ }
454
+ async function evaluatePredicateRaw(session, predicate, since = 0,
423
455
  // Compute the (extra-round-trip) near-miss diagnostics on element failures. Default true so a
424
456
  // one-shot assert is fully diagnostic; the wait loop passes false on its interim polls (which read
425
457
  // only `pass`) and true on the final timeout eval, so a flood no longer pays for a diagnostic nobody
@@ -760,11 +792,11 @@ export function waitForPredicate(session, predicate, timeoutMs, since = 0) {
760
792
  // `{ pass, evidence, failureReason }` construction DISCARDED them on every timed-out wait and
761
793
  // assert. So the highest-value localization signal was computed and then thrown away exactly
762
794
  // on the failure path where it matters, no matter what the schema declared.
763
- finish({
795
+ finish(annotateThrottledMiss(session, {
764
796
  ...r,
765
797
  pass: false,
766
798
  failureReason: r.failureReason ?? 'timed out waiting for predicate',
767
- });
799
+ }));
768
800
  })
769
801
  .catch((error) => {
770
802
  finish(failed(error));
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Turning "no element matched {text: …}" into something an agent can act on, when the string IS here.
3
+ *
4
+ * `by: text` matches an element's own text nodes. A label rendered across several children —
5
+ * `v-html`, one `<span>` per word, a highlighted substring — is on the page and matches nothing, and
6
+ * the verdict is byte-identical to the one for an element that never rendered.
7
+ *
8
+ * That equivalence is the expensive part. Reported from the field: `act_and_wait` returned
9
+ * `effect.appeared` containing `"Move to Reticle Repro Folder"`, the `until` predicate in the SAME
10
+ * call searched for that string and failed, and the drive ended in a bug report against an app that
11
+ * had rendered correctly. `appeared` concatenates an added subtree while `text` reads direct text
12
+ * only, so the channel an agent is most likely to copy from produces the one locator this query
13
+ * cannot resolve.
14
+ *
15
+ * Naming the container ends it in one step: `ref` is a locator, and `self: true` returns the scope
16
+ * element itself, so the recovery is a query the agent can paste rather than another guess.
17
+ */
18
+ /** The container descriptor the browser attaches to a missed text query. */
19
+ export interface SplitTextOwner {
20
+ ref?: string;
21
+ role?: string;
22
+ name?: string;
23
+ }
24
+ /**
25
+ * The "…but it IS here, split across children" clause, or undefined when there is nothing to say.
26
+ *
27
+ * Undefined — not a vaguer sentence — when the browser found no container: an ordinary miss keeps the
28
+ * short message it already had. A clause that fires on every failure stops carrying information.
29
+ */
30
+ export declare function describeSplitTextMiss(owner: SplitTextOwner | undefined): string | undefined;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Turning "no element matched {text: …}" into something an agent can act on, when the string IS here.
3
+ *
4
+ * `by: text` matches an element's own text nodes. A label rendered across several children —
5
+ * `v-html`, one `<span>` per word, a highlighted substring — is on the page and matches nothing, and
6
+ * the verdict is byte-identical to the one for an element that never rendered.
7
+ *
8
+ * That equivalence is the expensive part. Reported from the field: `act_and_wait` returned
9
+ * `effect.appeared` containing `"Move to Reticle Repro Folder"`, the `until` predicate in the SAME
10
+ * call searched for that string and failed, and the drive ended in a bug report against an app that
11
+ * had rendered correctly. `appeared` concatenates an added subtree while `text` reads direct text
12
+ * only, so the channel an agent is most likely to copy from produces the one locator this query
13
+ * cannot resolve.
14
+ *
15
+ * Naming the container ends it in one step: `ref` is a locator, and `self: true` returns the scope
16
+ * element itself, so the recovery is a query the agent can paste rather than another guess.
17
+ */
18
+ /** Enough of the label to recognise it; the message is read in a tool result, not a log. */
19
+ const MAX_TEXT = 60;
20
+ /** What the container is, in the fewest words that still identify it on the page. */
21
+ function nameOf(owner) {
22
+ const name = owner.name !== undefined && owner.name.length > 0 ? owner.name : undefined;
23
+ const role = owner.role !== undefined && owner.role.length > 0 ? owner.role : 'element';
24
+ return name === undefined ? role : `${role} '${truncate(name)}'`;
25
+ }
26
+ function truncate(value) {
27
+ return value.length > MAX_TEXT ? `${value.slice(0, MAX_TEXT)}…` : value;
28
+ }
29
+ /**
30
+ * The "…but it IS here, split across children" clause, or undefined when there is nothing to say.
31
+ *
32
+ * Undefined — not a vaguer sentence — when the browser found no container: an ordinary miss keeps the
33
+ * short message it already had. A clause that fires on every failure stops carrying information.
34
+ */
35
+ export function describeSplitTextMiss(owner) {
36
+ if (owner === undefined)
37
+ return undefined;
38
+ const where = nameOf(owner);
39
+ const scope = owner.ref !== undefined && owner.ref.length > 0 ? owner.ref : undefined;
40
+ const recovery = scope === undefined
41
+ ? 'scope to that container and match it as a whole'
42
+ : `retry as { scope: '${scope}', self: true }`;
43
+ return `that text IS on the page, split across the children of ${where} — no single element's own text carries it, so no text query can match it; ${recovery}`;
44
+ }
@@ -9,11 +9,11 @@ declare const FlakeRecordSchema: z.ZodObject<{
9
9
  runs: z.ZodNumber;
10
10
  fails: z.ZodNumber;
11
11
  }, "strip", z.ZodTypeAny, {
12
- fails: number;
13
12
  runs: number;
14
- }, {
15
13
  fails: number;
14
+ }, {
16
15
  runs: number;
16
+ fails: number;
17
17
  }>;
18
18
  export type FlakeRecord = z.infer<typeof FlakeRecordSchema>;
19
19
  /** Per-flow map (flow name → record). */
@@ -24,23 +24,23 @@ export declare const FlakeFileSchema: z.ZodObject<{
24
24
  runs: z.ZodNumber;
25
25
  fails: z.ZodNumber;
26
26
  }, "strip", z.ZodTypeAny, {
27
- fails: number;
28
27
  runs: number;
29
- }, {
30
28
  fails: number;
29
+ }, {
31
30
  runs: number;
31
+ fails: number;
32
32
  }>>;
33
33
  }, "strip", z.ZodTypeAny, {
34
34
  version: 1;
35
35
  flows: Record<string, {
36
- fails: number;
37
36
  runs: number;
37
+ fails: number;
38
38
  }>;
39
39
  }, {
40
40
  version: 1;
41
41
  flows: Record<string, {
42
- fails: number;
43
42
  runs: number;
43
+ fails: number;
44
44
  }>;
45
45
  }>;
46
46
  export declare function emptyRecord(): FlakeRecord;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * What the team already knows about the flow being verified.
3
+ *
4
+ * ## Why a replay should ask at all
5
+ *
6
+ * Shared memory is written on every verification and, measured across this project's whole corpus,
7
+ * read essentially never: every subject on the dashboard showed `fetches: 0` and `readers: []`. Not
8
+ * because the data is useless — because consulting it is a SEPARATE act an agent has to remember to
9
+ * perform. Knowledge that must be deliberately fetched is a wiki, and wikis are not read.
10
+ *
11
+ * So verification asks on the agent's behalf. Replaying `checkout` returns what the project knows
12
+ * about checkout beside the verdict, which is the difference between memory the platform stores and
13
+ * memory the platform USES — and it is the only way the read counts on the coverage map ever become
14
+ * a real signal rather than a number nobody moves.
15
+ *
16
+ * ## Why this file is pure
17
+ *
18
+ * The fetch is the caller's. What a replay is ENTITLED to ask for — which subject, how much of it —
19
+ * is a rule worth testing without a network, and the rule is where the mistakes live.
20
+ */
21
+ import type { FlowFile } from '@reticlehq/core';
22
+ /**
23
+ * How many records travel back with a verdict.
24
+ *
25
+ * Small on purpose. This rides on every replay, and a verdict that arrives with fifty statements
26
+ * attached is one whose actual result is now below the fold. The subject page is one call away for
27
+ * an agent that wants the rest.
28
+ */
29
+ export declare const MEMORY_CONSULT_LIMIT = 5;
30
+ /**
31
+ * The subject a replay should ask about.
32
+ *
33
+ * The FLOW NAME, because a flow is a feature and that is the axis the store shards on — replaying
34
+ * `checkout` should surface what the team knows about checkout, not about the route it happens to
35
+ * start on. A flow whose name says nothing gets nothing: asking for `unsorted` would return the
36
+ * project's junk drawer on every verdict, which is worse than silence because it looks like an
37
+ * answer.
38
+ */
39
+ export declare function consultSubjectFor(flow: FlowFile): string | undefined;
40
+ /** One thing the project knows, as it travels back with a verdict. */
41
+ export interface ConsultedMemory {
42
+ statement: string;
43
+ status: string;
44
+ }
45
+ /**
46
+ * Trim what came back to what is worth carrying.
47
+ *
48
+ * PROVED records first: a statement something has actually verified is stronger evidence about the
49
+ * feature than one somebody proposed and never checked. Beyond that the order is the server's, and
50
+ * anything without a statement is dropped rather than rendered as an empty line.
51
+ */
52
+ export declare function selectConsulted(entries: readonly {
53
+ statement?: unknown;
54
+ status?: unknown;
55
+ }[], limit?: number): ConsultedMemory[];
@@ -0,0 +1,38 @@
1
+ /**
2
+ * How many records travel back with a verdict.
3
+ *
4
+ * Small on purpose. This rides on every replay, and a verdict that arrives with fifty statements
5
+ * attached is one whose actual result is now below the fold. The subject page is one call away for
6
+ * an agent that wants the rest.
7
+ */
8
+ export const MEMORY_CONSULT_LIMIT = 5;
9
+ /**
10
+ * The subject a replay should ask about.
11
+ *
12
+ * The FLOW NAME, because a flow is a feature and that is the axis the store shards on — replaying
13
+ * `checkout` should surface what the team knows about checkout, not about the route it happens to
14
+ * start on. A flow whose name says nothing gets nothing: asking for `unsorted` would return the
15
+ * project's junk drawer on every verdict, which is worse than silence because it looks like an
16
+ * answer.
17
+ */
18
+ export function consultSubjectFor(flow) {
19
+ const name = flow.name;
20
+ if ('string' !== typeof name || 0 === name.trim().length)
21
+ return undefined;
22
+ return name.trim();
23
+ }
24
+ /**
25
+ * Trim what came back to what is worth carrying.
26
+ *
27
+ * PROVED records first: a statement something has actually verified is stronger evidence about the
28
+ * feature than one somebody proposed and never checked. Beyond that the order is the server's, and
29
+ * anything without a statement is dropped rather than rendered as an empty line.
30
+ */
31
+ export function selectConsulted(entries, limit = MEMORY_CONSULT_LIMIT) {
32
+ const usable = entries.flatMap((e) => 'string' === typeof e.statement && e.statement.trim().length > 0
33
+ ? [{ statement: e.statement, status: 'string' === typeof e.status ? e.status : 'unknown' }]
34
+ : []);
35
+ const proved = usable.filter((e) => 'proved' === e.status);
36
+ const rest = usable.filter((e) => 'proved' !== e.status);
37
+ return [...proved, ...rest].slice(0, limit);
38
+ }
@@ -1,4 +1,7 @@
1
- import { FlowErrorCode, type FlowFile, type FlowReplayResult, type ReticleEvent } from '@reticlehq/core';
1
+ import { FlowErrorCode, type CommandResult, type FlowFile, type FlowReplayResult, type ReticleEvent } from '@reticlehq/core';
2
+ import type { ArrivalClock } from '../tools/navigate-arrival.js';
3
+ import type { SessionManager } from '../session/session-manager.js';
4
+ import type { Session } from '../session/session.js';
2
5
  import type { ToolDeps } from '../tools/tools.js';
3
6
  export declare function latestRecordedFlow(events: ReticleEvent[]): {
4
7
  name: string;
@@ -6,18 +9,25 @@ export declare function latestRecordedFlow(events: ReticleEvent[]): {
6
9
  } | undefined;
7
10
  /** Map a structured FlowErrorCode to a legible one-line message for the agent. */
8
11
  export declare function flowErrorMessage(code: FlowErrorCode): string;
12
+ /** The slice of a session the start-path logic reads: the live URL plus the event buffer. */
13
+ interface StartPathSession {
14
+ url?: string;
15
+ eventsSince(cursor: number): ReticleEvent[];
16
+ }
9
17
  /**
10
18
  * When a flow records the page its journey started on (`startPath`) and the tab is currently on a
11
19
  * different route, step 1 drifts for a reason that has nothing to do with the app regressing — the
12
20
  * anchor simply isn't on this page yet. Detect that so the decision says "navigate there first"
13
21
  * instead of a mystifying "a step no longer matches". Returns undefined when the routes agree or the
14
- * current route is unobservable (no route event) — never a false alarm. Replay itself does NOT
15
- * navigate: a full-page load mid-replay tears down the session socket; the agent navigates between
16
- * tool calls (reticle_navigate) where the session is re-resolved fresh.
22
+ * current route is unobservable — never a false alarm. Replay normally never gets here on a
23
+ * wrong page (arriveAtStartPath navigates before step 1); this is the fallback for when that
24
+ * navigation was refused or the SDK never reconnected in the window.
17
25
  */
18
- export declare function startPathMismatchHint(flow: FlowFile, session: {
19
- eventsSince(cursor: number): ReticleEvent[];
20
- }): string | undefined;
26
+ export declare function startPathMismatchHint(flow: FlowFile, session: StartPathSession): string | undefined;
27
+ export declare function arriveAtStartPath(sessions: SessionManager, session: StartPathSession & {
28
+ id: string;
29
+ command(name: string, args?: Record<string, unknown>): Promise<CommandResult>;
30
+ }, flow: FlowFile, timeoutMs?: number, clock?: ArrivalClock): Promise<Session | undefined>;
21
31
  /**
22
32
  * Replay one named flow end to end: load → re-resolve+run each step → assert the success oracle →
23
33
  * status + decision. Shared by reticle_flow_replay (single flow) and reticle_flow_verify (whole suite) so
@@ -30,3 +40,4 @@ export declare function replayNamedFlow(deps: ToolDeps, args: Record<string, unk
30
40
  * documented to work headless), so a missing/unknown session degrades to the global/legacy store.
31
41
  */
32
42
  export declare function sessionProjectId(deps: ToolDeps, sessionId: string | undefined): string | undefined;
43
+ export {};