@reticlehq/server 2.11.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (513) hide show
  1. package/SKILL.md +93 -111
  2. package/dist/bridge/auth-failure-reason.d.ts +6 -2
  3. package/dist/bridge/auth-failure-reason.js +15 -8
  4. package/dist/bridge/bridge.d.ts +18 -14
  5. package/dist/bridge/bridge.js +248 -165
  6. package/dist/bridge/bridge.test-harness.d.ts +2 -0
  7. package/dist/bridge/bridge.test-harness.js +8 -1
  8. package/dist/bridge/pairing-token.d.ts +5 -0
  9. package/dist/bridge/pairing-token.js +28 -0
  10. package/dist/bridge/protocol-skew.d.ts +37 -0
  11. package/dist/bridge/protocol-skew.js +45 -0
  12. package/dist/capsule/capsule-store.d.ts +8 -7
  13. package/dist/capsule/capsule-store.js +1 -1
  14. package/dist/capsule/causal-summary.d.ts +2 -2
  15. package/dist/capsule/causal-summary.js +7 -2
  16. package/dist/chromium-launch-options.d.ts +28 -0
  17. package/dist/chromium-launch-options.js +29 -0
  18. package/dist/cli/cli-doctor.js +39 -1
  19. package/dist/cli/cli-flow-commands.d.ts +3 -1
  20. package/dist/cli/cli-flow-commands.js +34 -4
  21. package/dist/cli/cli-kill.d.ts +2 -1
  22. package/dist/cli/cli-launch.d.ts +11 -1
  23. package/dist/cli/cli-launch.js +59 -10
  24. package/dist/cli/cli-parse.d.ts +17 -1
  25. package/dist/cli/cli-parse.js +238 -12
  26. package/dist/cli/cli-update-commands.js +20 -1
  27. package/dist/cli/cli-verify.d.ts +2 -1
  28. package/dist/cli/cli-verify.js +5 -4
  29. package/dist/cli/cloud-cli.js +428 -253
  30. package/dist/cli/cloud-keystore.d.ts +37 -0
  31. package/dist/cli/cloud-keystore.js +97 -0
  32. package/dist/cli/cloud-kit.d.ts +37 -0
  33. package/dist/cli/cloud-kit.js +121 -0
  34. package/dist/cli/cloud-login.d.ts +17 -0
  35. package/dist/cli/cloud-login.js +183 -0
  36. package/dist/cli/cloud-session.d.ts +48 -0
  37. package/dist/cli/cloud-session.js +53 -0
  38. package/dist/cli/config-discovery.d.ts +2 -1
  39. package/dist/cli/config-discovery.js +1 -0
  40. package/dist/cli/daemon-start-options.d.ts +32 -0
  41. package/dist/cli/daemon-start-options.js +38 -0
  42. package/dist/cli/device-flow.d.ts +76 -0
  43. package/dist/cli/device-flow.js +56 -0
  44. package/dist/cli/doctor-rows.d.ts +12 -1
  45. package/dist/cli/doctor-rows.js +12 -1
  46. package/dist/cli/drive-attach.d.ts +13 -0
  47. package/dist/cli/drive-attach.js +18 -0
  48. package/dist/cli/drive-command.js +7 -3
  49. package/dist/cli/gate-exit.d.ts +21 -0
  50. package/dist/cli/gate-exit.js +21 -0
  51. package/dist/cli/gate-hook-message.d.ts +33 -0
  52. package/dist/cli/gate-hook-message.js +46 -0
  53. package/dist/cli/mcp-command.d.ts +25 -0
  54. package/dist/cli/mcp-command.js +174 -0
  55. package/dist/cli/project-name.d.ts +30 -0
  56. package/dist/cli/project-name.js +44 -0
  57. package/dist/cli/sibling-ports.d.ts +37 -0
  58. package/dist/cli/sibling-ports.js +56 -0
  59. package/dist/cli/status-next-action.d.ts +14 -0
  60. package/dist/cli/status-next-action.js +4 -4
  61. package/dist/cli.d.ts +1 -0
  62. package/dist/cli.js +167 -186
  63. package/dist/cloud/cloud-config.d.ts +24 -2
  64. package/dist/cloud/cloud-config.js +64 -5
  65. package/dist/cloud/cloud-sync.d.ts +16 -5
  66. package/dist/cloud/cloud-sync.js +64 -1
  67. package/dist/cloud/sync-cycle.d.ts +110 -0
  68. package/dist/cloud/sync-cycle.js +270 -0
  69. package/dist/cloud/sync-daemon.d.ts +69 -0
  70. package/dist/cloud/sync-daemon.js +260 -0
  71. package/dist/cloud/sync-disk.d.ts +9 -0
  72. package/dist/cloud/sync-disk.js +191 -0
  73. package/dist/cloud/sync-hash.d.ts +2 -0
  74. package/dist/cloud/sync-hash.js +35 -0
  75. package/dist/crawl/crawl-tools.js +5 -3
  76. package/dist/crawl/crawl.d.ts +12 -13
  77. package/dist/crawl/crawl.js +25 -3
  78. package/dist/daemon/daemon-resolve.d.ts +122 -0
  79. package/dist/daemon/daemon-resolve.js +187 -0
  80. package/dist/daemon/dev-servers.d.ts +15 -0
  81. package/dist/daemon/dev-servers.js +68 -0
  82. package/dist/daemon/free-port.d.ts +29 -0
  83. package/dist/daemon/free-port.js +55 -0
  84. package/dist/daemon/heartbeat.d.ts +0 -4
  85. package/dist/daemon/heartbeat.js +2 -2
  86. package/dist/daemon/wake-decision.d.ts +32 -0
  87. package/dist/daemon/wake-decision.js +38 -0
  88. package/dist/dev/stale-issue-guard.d.ts +0 -2
  89. package/dist/dev/stale-issue-guard.js +1 -1
  90. package/dist/domain/domain-tools.js +2 -2
  91. package/dist/events/contradictions.d.ts +59 -0
  92. package/dist/events/contradictions.js +253 -27
  93. package/dist/events/declared.d.ts +17 -0
  94. package/dist/events/declared.js +43 -1
  95. package/dist/events/echo-mismatch.js +65 -1
  96. package/dist/events/event-filters.d.ts +8 -0
  97. package/dist/events/event-filters.js +19 -2
  98. package/dist/events/observed-in-window.d.ts +12 -0
  99. package/dist/events/observed-in-window.js +17 -0
  100. package/dist/events/predicate-asks.d.ts +13 -0
  101. package/dist/events/predicate-asks.js +22 -0
  102. package/dist/events/predicate-eval.js +117 -27
  103. package/dist/events/predicate-parse.d.ts +0 -6
  104. package/dist/events/predicate-parse.js +43 -3
  105. package/dist/events/predicate-precheck.d.ts +8 -0
  106. package/dist/events/predicate-precheck.js +41 -0
  107. package/dist/events/predicate-route.d.ts +14 -0
  108. package/dist/events/predicate-route.js +36 -8
  109. package/dist/events/predicate-schema.d.ts +44 -0
  110. package/dist/events/predicate-schema.js +126 -23
  111. package/dist/events/predicate.d.ts +20 -0
  112. package/dist/events/predicate.js +77 -6
  113. package/dist/events/split-text-miss.d.ts +30 -0
  114. package/dist/events/split-text-miss.js +44 -0
  115. package/dist/flows/coverage.d.ts +0 -8
  116. package/dist/flows/coverage.js +1 -1
  117. package/dist/flows/decision.d.ts +5 -1
  118. package/dist/flows/decision.js +33 -7
  119. package/dist/flows/flake.d.ts +6 -8
  120. package/dist/flows/flake.js +1 -1
  121. package/dist/flows/flow-intent.d.ts +52 -0
  122. package/dist/flows/flow-intent.js +123 -0
  123. package/dist/flows/flow-memory-consult.d.ts +55 -0
  124. package/dist/flows/flow-memory-consult.js +38 -0
  125. package/dist/flows/flow-replay-run.d.ts +18 -7
  126. package/dist/flows/flow-replay-run.js +263 -23
  127. package/dist/flows/flow-replay.js +11 -2
  128. package/dist/flows/flow-step-runners.d.ts +8 -0
  129. package/dist/flows/flow-step-runners.js +8 -2
  130. package/dist/flows/flow-store-for-session.d.ts +31 -0
  131. package/dist/flows/flow-store-for-session.js +33 -0
  132. package/dist/flows/flow-success.js +15 -3
  133. package/dist/flows/flow-tools.js +87 -33
  134. package/dist/flows/flows.d.ts +14 -1
  135. package/dist/flows/flows.js +502 -24
  136. package/dist/flows/heal-run.js +5 -2
  137. package/dist/flows/predicate-to-expect.d.ts +17 -12
  138. package/dist/flows/predicate-to-expect.js +36 -15
  139. package/dist/flows/recordings.d.ts +13 -1
  140. package/dist/flows/recordings.js +17 -2
  141. package/dist/flows/replay.d.ts +22 -2
  142. package/dist/flows/replay.js +28 -2
  143. package/dist/flows/server-verify.js +16 -2
  144. package/dist/flows/verify-change-tools.js +9 -3
  145. package/dist/honesty/blind-spots.d.ts +51 -15
  146. package/dist/honesty/blind-spots.js +64 -4
  147. package/dist/honesty/feature-capture.d.ts +150 -0
  148. package/dist/honesty/feature-capture.js +233 -0
  149. package/dist/honesty/honesty.d.ts +24 -0
  150. package/dist/honesty/honesty.js +25 -1
  151. package/dist/honesty/instrumentation-gaps.d.ts +39 -6
  152. package/dist/honesty/instrumentation-gaps.js +88 -5
  153. package/dist/honesty/tool-hit-rate.d.ts +86 -0
  154. package/dist/honesty/tool-hit-rate.js +90 -0
  155. package/dist/honesty/undeclared-change.d.ts +51 -0
  156. package/dist/honesty/undeclared-change.js +56 -0
  157. package/dist/honesty/verified.d.ts +16 -0
  158. package/dist/honesty/verified.js +84 -5
  159. package/dist/http-server.d.ts +0 -1
  160. package/dist/http-server.js +13 -1
  161. package/dist/hunt/hunt-report.d.ts +0 -2
  162. package/dist/hunt/hunt-report.js +1 -1
  163. package/dist/impact/impact-recorder.d.ts +31 -8
  164. package/dist/impact/impact-recorder.js +104 -21
  165. package/dist/impact/impact-store.d.ts +13 -4
  166. package/dist/impact/impact-store.js +44 -3
  167. package/dist/index.d.ts +4 -1
  168. package/dist/index.js +113 -7
  169. package/dist/init/agent-rules.d.ts +30 -6
  170. package/dist/init/agent-rules.js +54 -13
  171. package/dist/init/astro-patch.d.ts +16 -0
  172. package/dist/init/astro-patch.js +28 -0
  173. package/dist/init/capabilities.d.ts +4 -3
  174. package/dist/init/capabilities.js +23 -3
  175. package/dist/init/closing-hint.d.ts +36 -0
  176. package/dist/init/closing-hint.js +120 -0
  177. package/dist/init/confirm.d.ts +27 -28
  178. package/dist/init/confirm.js +51 -11
  179. package/dist/init/cra.d.ts +34 -5
  180. package/dist/init/cra.js +96 -25
  181. package/dist/init/csp-check.js +17 -2
  182. package/dist/init/csp-doctor.d.ts +8 -1
  183. package/dist/init/csp-doctor.js +8 -3
  184. package/dist/init/dev-script.d.ts +50 -0
  185. package/dist/init/dev-script.js +66 -0
  186. package/dist/init/format-generated.d.ts +18 -0
  187. package/dist/init/format-generated.js +39 -0
  188. package/dist/init/install-hint.d.ts +9 -0
  189. package/dist/init/install-hint.js +29 -0
  190. package/dist/init/mcp-clients.d.ts +7 -0
  191. package/dist/init/mcp-clients.js +7 -0
  192. package/dist/init/mcp.d.ts +17 -6
  193. package/dist/init/mcp.js +27 -11
  194. package/dist/init/node-io.js +11 -1
  195. package/dist/init/plan-framework.d.ts +0 -5
  196. package/dist/init/plan-framework.js +147 -37
  197. package/dist/init/plan.d.ts +30 -37
  198. package/dist/init/plan.js +50 -60
  199. package/dist/init/preflight.d.ts +28 -0
  200. package/dist/init/preflight.js +38 -0
  201. package/dist/init/refresh-rules.d.ts +39 -0
  202. package/dist/init/refresh-rules.js +51 -0
  203. package/dist/init/run.d.ts +32 -35
  204. package/dist/init/run.js +151 -202
  205. package/dist/init/slash-command.d.ts +1 -1
  206. package/dist/init/snippets.js +90 -31
  207. package/dist/init/vite-config.d.ts +1 -1
  208. package/dist/init/vite-config.js +39 -25
  209. package/dist/init/workspace-apps.d.ts +17 -10
  210. package/dist/init/workspace-apps.js +110 -0
  211. package/dist/input/network-mock-tools.js +3 -6
  212. package/dist/input/real-input.d.ts +2 -0
  213. package/dist/input/real-input.js +28 -7
  214. package/dist/input/scroll-find.d.ts +7 -4
  215. package/dist/input/scroll-find.js +63 -25
  216. package/dist/input/scroll-tools.js +4 -6
  217. package/dist/input/viewport-tools.js +5 -4
  218. package/dist/intent/inline-intent.d.ts +37 -0
  219. package/dist/intent/inline-intent.js +193 -0
  220. package/dist/intent/intent-shard-store.d.ts +72 -0
  221. package/dist/intent/intent-shard-store.js +203 -0
  222. package/dist/intent/intent-shard.d.ts +411 -0
  223. package/dist/intent/intent-shard.js +176 -0
  224. package/dist/intent/intent-store.d.ts +49 -0
  225. package/dist/intent/intent-store.js +133 -0
  226. package/dist/intent/intent-subject.d.ts +45 -0
  227. package/dist/intent/intent-subject.js +177 -0
  228. package/dist/intent/intent-tools.d.ts +2 -0
  229. package/dist/intent/intent-tools.js +143 -0
  230. package/dist/intent/open-intents.d.ts +42 -0
  231. package/dist/intent/open-intents.js +40 -0
  232. package/dist/journal/envelope.d.ts +26 -26
  233. package/dist/journal/journal-recorder.d.ts +23 -0
  234. package/dist/journal/journal-recorder.js +35 -0
  235. package/dist/license/license-env.d.ts +29 -0
  236. package/dist/license/license-env.js +148 -0
  237. package/dist/loopback-agent.d.ts +31 -0
  238. package/dist/loopback-agent.js +57 -0
  239. package/dist/mcp/attach-memory.d.ts +83 -0
  240. package/dist/mcp/attach-memory.js +185 -0
  241. package/dist/mcp/client-identity.d.ts +31 -0
  242. package/dist/mcp/client-identity.js +51 -0
  243. package/dist/mcp/mcp-post-transport.d.ts +26 -0
  244. package/dist/mcp/mcp-post-transport.js +109 -0
  245. package/dist/mcp/mcp-proxy.d.ts +2 -8
  246. package/dist/mcp/mcp-proxy.js +64 -62
  247. package/dist/mcp/mcp.d.ts +2 -0
  248. package/dist/mcp/mcp.js +52 -24
  249. package/dist/mcp/proxy-backoff.d.ts +11 -0
  250. package/dist/mcp/proxy-backoff.js +13 -0
  251. package/dist/mcp/proxy-daemon-probe.d.ts +1 -0
  252. package/dist/mcp/proxy-daemon-probe.js +7 -1
  253. package/dist/mcp/proxy-handshake.d.ts +23 -0
  254. package/dist/mcp/proxy-handshake.js +31 -0
  255. package/dist/mcp/server-instructions.js +45 -3
  256. package/dist/mcp/tool-catalog-cache.d.ts +16 -0
  257. package/dist/mcp/tool-catalog-cache.js +22 -0
  258. package/dist/memory/memory-tools.d.ts +2 -0
  259. package/dist/memory/memory-tools.js +82 -0
  260. package/dist/memory/project-memory.d.ts +34 -0
  261. package/dist/memory/project-memory.js +96 -0
  262. package/dist/oracles/flow-instrument-gaps.d.ts +4 -4
  263. package/dist/oracles/flow-instrument-gaps.js +4 -5
  264. package/dist/oracles/propose-consequences.js +12 -3
  265. package/dist/oracles/self-instrument.d.ts +9 -9
  266. package/dist/oracles/self-instrument.js +19 -13
  267. package/dist/pool/browser-pool.d.ts +75 -5
  268. package/dist/pool/browser-pool.js +144 -5
  269. package/dist/pool/playwright-launcher.js +12 -1
  270. package/dist/project/fs-port.d.ts +3 -0
  271. package/dist/project/fs-port.js +3 -2
  272. package/dist/project/memory-fs.js +6 -1
  273. package/dist/project/project-tools.js +6 -35
  274. package/dist/project/reticle-dir.d.ts +17 -2
  275. package/dist/project/reticle-dir.js +22 -4
  276. package/dist/runs/context-tools.d.ts +2 -0
  277. package/dist/runs/context-tools.js +89 -0
  278. package/dist/runs/run-context.d.ts +40 -0
  279. package/dist/runs/run-context.js +158 -0
  280. package/dist/runs/run-diff.d.ts +2 -1
  281. package/dist/runs/run-store.d.ts +10 -0
  282. package/dist/runs/run-store.js +10 -0
  283. package/dist/runs/verification-sync.d.ts +0 -11
  284. package/dist/runs/verification-sync.js +24 -3
  285. package/dist/runs/verify-server.d.ts +8 -1
  286. package/dist/runs/verify-server.js +14 -2
  287. package/dist/session/command-timeout.d.ts +5 -2
  288. package/dist/session/command-timeout.js +5 -4
  289. package/dist/session/dev-server-probe.d.ts +0 -8
  290. package/dist/session/dev-server-probe.js +1 -1
  291. package/dist/session/last-act.d.ts +7 -1
  292. package/dist/session/last-act.js +2 -2
  293. package/dist/session/lease-visibility.d.ts +67 -0
  294. package/dist/session/lease-visibility.js +76 -0
  295. package/dist/session/live-control-tools.js +1 -42
  296. package/dist/session/no-session-diagnosis.d.ts +33 -0
  297. package/dist/session/no-session-diagnosis.js +82 -24
  298. package/dist/session/no-session-next-action.d.ts +12 -0
  299. package/dist/session/no-session-next-action.js +77 -6
  300. package/dist/session/no-session-watch.d.ts +18 -6
  301. package/dist/session/no-session-watch.js +125 -75
  302. package/dist/session/output-budget.js +4 -1
  303. package/dist/session/pending-commands.d.ts +11 -1
  304. package/dist/session/pending-commands.js +12 -2
  305. package/dist/session/resolve-within.d.ts +34 -0
  306. package/dist/session/resolve-within.js +53 -0
  307. package/dist/session/session-health.d.ts +9 -0
  308. package/dist/session/session-health.js +23 -0
  309. package/dist/session/session-info.d.ts +18 -0
  310. package/dist/session/session-info.js +11 -0
  311. package/dist/session/session-manager.d.ts +14 -0
  312. package/dist/session/session-manager.js +111 -4
  313. package/dist/session/session-mirror.d.ts +16 -0
  314. package/dist/session/session-mirror.js +22 -0
  315. package/dist/session/session-recommendation.d.ts +19 -5
  316. package/dist/session/session-recommendation.js +25 -9
  317. package/dist/session/session-successor.d.ts +37 -0
  318. package/dist/session/session-successor.js +113 -0
  319. package/dist/session/session-tools.js +2 -2
  320. package/dist/session/session.d.ts +53 -3
  321. package/dist/session/session.js +162 -9
  322. package/dist/session/wall-clock.d.ts +12 -0
  323. package/dist/session/wall-clock.js +14 -0
  324. package/dist/setup/agent-configs.d.ts +96 -0
  325. package/dist/setup/agent-configs.js +333 -0
  326. package/dist/setup/agent-io.d.ts +9 -0
  327. package/dist/setup/agent-io.js +16 -0
  328. package/dist/setup/agent-writer.d.ts +51 -0
  329. package/dist/setup/agent-writer.js +115 -0
  330. package/dist/setup/approval-migration.d.ts +47 -0
  331. package/dist/setup/approval-migration.js +68 -0
  332. package/dist/setup/auto-approve.d.ts +85 -0
  333. package/dist/setup/auto-approve.js +150 -0
  334. package/dist/setup/bridge-port.d.ts +15 -0
  335. package/dist/setup/bridge-port.js +24 -0
  336. package/dist/setup/desktop-shape.d.ts +57 -0
  337. package/dist/setup/desktop-shape.js +63 -0
  338. package/dist/setup/dev-server-wait.d.ts +60 -0
  339. package/dist/setup/dev-server-wait.js +65 -0
  340. package/dist/setup/drive-agent.d.ts +71 -0
  341. package/dist/setup/drive-agent.js +171 -0
  342. package/dist/setup/drive-plan.d.ts +68 -0
  343. package/dist/setup/drive-plan.js +130 -0
  344. package/dist/setup/ensure-daemon.d.ts +29 -0
  345. package/dist/setup/ensure-daemon.js +58 -0
  346. package/dist/setup/init-runtime.d.ts +47 -0
  347. package/dist/setup/init-runtime.js +160 -0
  348. package/dist/setup/interrupt.d.ts +25 -0
  349. package/dist/setup/interrupt.js +40 -0
  350. package/dist/setup/license-key.d.ts +39 -0
  351. package/dist/setup/license-key.js +72 -0
  352. package/dist/setup/listeners.d.ts +37 -0
  353. package/dist/setup/listeners.js +85 -0
  354. package/dist/setup/node-effects.d.ts +60 -0
  355. package/dist/setup/node-effects.js +210 -0
  356. package/dist/setup/page-probe.d.ts +39 -0
  357. package/dist/setup/page-probe.js +49 -0
  358. package/dist/setup/relaunch.d.ts +36 -0
  359. package/dist/setup/relaunch.js +57 -0
  360. package/dist/setup/remaining-steps.d.ts +19 -0
  361. package/dist/setup/remaining-steps.js +31 -0
  362. package/dist/setup/run-setup.d.ts +99 -0
  363. package/dist/setup/run-setup.js +187 -0
  364. package/dist/setup/session-pick.d.ts +47 -0
  365. package/dist/setup/session-pick.js +51 -0
  366. package/dist/setup/setup-command.d.ts +45 -0
  367. package/dist/setup/setup-command.js +199 -0
  368. package/dist/setup/setup-options.d.ts +74 -0
  369. package/dist/setup/setup-options.js +67 -0
  370. package/dist/setup/transcripts.d.ts +16 -0
  371. package/dist/setup/transcripts.js +43 -0
  372. package/dist/status-payload.d.ts +14 -1
  373. package/dist/status-payload.js +23 -1
  374. package/dist/telemetry/automation-hint.d.ts +2 -1
  375. package/dist/telemetry/daemon-telemetry.d.ts +8 -1
  376. package/dist/telemetry/daemon-telemetry.js +52 -5
  377. package/dist/telemetry/error-class.js +1 -1
  378. package/dist/telemetry/feedback-context.d.ts +9 -1
  379. package/dist/telemetry/feedback-context.js +60 -29
  380. package/dist/telemetry/feedback-local.d.ts +15 -0
  381. package/dist/telemetry/feedback-local.js +56 -0
  382. package/dist/telemetry/feedback-outbox.d.ts +2 -1
  383. package/dist/telemetry/feedback.d.ts +11 -0
  384. package/dist/telemetry/feedback.js +19 -6
  385. package/dist/telemetry/init-telemetry.d.ts +2 -0
  386. package/dist/telemetry/init-telemetry.js +2 -0
  387. package/dist/telemetry/install-source.d.ts +14 -0
  388. package/dist/telemetry/install-source.js +30 -0
  389. package/dist/telemetry/license-activation.d.ts +7 -1
  390. package/dist/telemetry/license-activation.js +9 -5
  391. package/dist/telemetry/mcp-connection.js +7 -0
  392. package/dist/telemetry/project-profile.d.ts +12 -9
  393. package/dist/telemetry/project-profile.js +12 -7
  394. package/dist/telemetry/proxy-telemetry.d.ts +3 -0
  395. package/dist/telemetry/proxy-telemetry.js +21 -0
  396. package/dist/telemetry/run-telemetry.d.ts +0 -2
  397. package/dist/telemetry/run-telemetry.js +1 -1
  398. package/dist/telemetry/session-metrics.d.ts +12 -0
  399. package/dist/telemetry/session-metrics.js +35 -1
  400. package/dist/telemetry/telemetry.js +17 -2
  401. package/dist/telemetry/tool-refused.d.ts +3 -1
  402. package/dist/telemetry/tool-refused.js +34 -2
  403. package/dist/telemetry/verification-of.js +2 -2
  404. package/dist/tools/act-capsule.d.ts +10 -0
  405. package/dist/tools/act-capsule.js +8 -2
  406. package/dist/tools/act-danger.d.ts +2 -1
  407. package/dist/tools/act-danger.js +11 -3
  408. package/dist/tools/act-observation.d.ts +41 -0
  409. package/dist/tools/act-observation.js +36 -0
  410. package/dist/tools/act-preflight.d.ts +20 -0
  411. package/dist/tools/act-preflight.js +98 -0
  412. package/dist/tools/act-sequence-retry.d.ts +45 -0
  413. package/dist/tools/act-sequence-retry.js +58 -0
  414. package/dist/tools/act-sequence-tool.d.ts +13 -0
  415. package/dist/tools/act-sequence-tool.js +135 -0
  416. package/dist/tools/act-target.d.ts +14 -0
  417. package/dist/tools/act-target.js +47 -0
  418. package/dist/tools/act-tools.d.ts +21 -1
  419. package/dist/tools/act-tools.js +196 -204
  420. package/dist/tools/assert-source.d.ts +22 -0
  421. package/dist/tools/assert-source.js +53 -0
  422. package/dist/tools/assert-verdict.d.ts +20 -3
  423. package/dist/tools/assert-verdict.js +58 -20
  424. package/dist/tools/browser-tools.js +6 -22
  425. package/dist/tools/complete-snapshot.d.ts +48 -0
  426. package/dist/tools/complete-snapshot.js +75 -0
  427. package/dist/tools/coverage-tools.d.ts +10 -1
  428. package/dist/tools/coverage-tools.js +123 -69
  429. package/dist/tools/dynamic-tools.js +53 -13
  430. package/dist/tools/error-recovery.d.ts +25 -14
  431. package/dist/tools/error-recovery.js +78 -9
  432. package/dist/tools/feedback-tools.d.ts +1 -1
  433. package/dist/tools/feedback-tools.js +2 -2
  434. package/dist/tools/invoke-tool.js +121 -11
  435. package/dist/tools/lease-hint.d.ts +10 -0
  436. package/dist/tools/lease-hint.js +53 -4
  437. package/dist/tools/lease-tools.d.ts +35 -5
  438. package/dist/tools/lease-tools.js +191 -13
  439. package/dist/tools/merged-name-redirect.d.ts +13 -0
  440. package/dist/tools/merged-name-redirect.js +22 -0
  441. package/dist/tools/navigate-arrival.d.ts +0 -2
  442. package/dist/tools/navigate-arrival.js +1 -1
  443. package/dist/tools/numeric-bounds.d.ts +100 -0
  444. package/dist/tools/numeric-bounds.js +125 -0
  445. package/dist/tools/observe-tools.js +109 -65
  446. package/dist/tools/read-tools.js +31 -8
  447. package/dist/tools/real-input-attempt.d.ts +32 -0
  448. package/dist/tools/real-input-attempt.js +212 -2
  449. package/dist/tools/reconcile-tools.js +22 -8
  450. package/dist/tools/resolve-target.d.ts +0 -7
  451. package/dist/tools/sdk-marker-probe.d.ts +29 -0
  452. package/dist/tools/sdk-marker-probe.js +57 -0
  453. package/dist/tools/snapshot-delta.d.ts +1 -1
  454. package/dist/tools/tool-kit.d.ts +24 -0
  455. package/dist/tools/tool-kit.js +21 -0
  456. package/dist/tools/tool-names.d.ts +5 -0
  457. package/dist/tools/tool-names.js +5 -0
  458. package/dist/tools/tool-surface.d.ts +85 -6
  459. package/dist/tools/tool-surface.js +122 -2
  460. package/dist/tools/tools-helpers.d.ts +19 -0
  461. package/dist/tools/tools-helpers.js +46 -0
  462. package/dist/tools/tools.d.ts +2 -0
  463. package/dist/tools/tools.js +78 -12
  464. package/dist/tools/unadvertised-help.js +4 -4
  465. package/dist/tools/verify-next-baton.d.ts +53 -0
  466. package/dist/tools/verify-next-baton.js +52 -0
  467. package/dist/update/reticle-deps.d.ts +9 -0
  468. package/dist/update/reticle-deps.js +27 -0
  469. package/dist/update/sdk-sync.d.ts +1 -2
  470. package/dist/update/sdk-sync.js +1 -21
  471. package/dist/update/update-nudge.d.ts +25 -10
  472. package/dist/update/update-nudge.js +32 -3
  473. package/dist/update/updater.d.ts +1 -1
  474. package/dist/version/peer-announce.js +4 -2
  475. package/dist/version/sdk-fix.d.ts +47 -0
  476. package/dist/version/sdk-fix.js +127 -0
  477. package/dist/version/version-nudge.d.ts +14 -0
  478. package/dist/version/version-nudge.js +21 -0
  479. package/dist/version/version-skew.d.ts +38 -3
  480. package/dist/version/version-skew.js +83 -7
  481. package/dist/visual/visual-store.d.ts +6 -6
  482. package/dist/visual/visual-store.js +18 -16
  483. package/dist/visual/visual-tools.js +57 -12
  484. package/docs/README.md +2 -1
  485. package/docs/cli/cloud.mdx +1 -1
  486. package/docs/cli/doctor.mdx +2 -0
  487. package/docs/cli/serve.mdx +4 -2
  488. package/docs/cli/verify.mdx +4 -5
  489. package/docs/cli.mdx +7 -3
  490. package/docs/desktop.mdx +1 -1
  491. package/docs/docs.json +2 -2
  492. package/docs/gates.md +1 -1
  493. package/docs/getting-started.md +19 -3
  494. package/docs/index.mdx +9 -6
  495. package/docs/install-agentic.mdx +38 -12
  496. package/docs/install-manual.mdx +1 -1
  497. package/docs/packages/browser.mdx +2 -2
  498. package/docs/packages/server.mdx +1 -1
  499. package/docs/packages.mdx +8 -6
  500. package/docs/platform-integration.md +17 -0
  501. package/docs/predicates.mdx +8 -0
  502. package/docs/reticle-manual.md +106 -0
  503. package/docs/skill-file.mdx +3 -3
  504. package/docs/telemetry-contract.md +108 -4
  505. package/docs/tools-act-sequence.mdx +16 -0
  506. package/docs/tools-tools-and-run.mdx +2 -4
  507. package/docs/troubleshooting.mdx +9 -0
  508. package/docs/usage.md +4 -3
  509. package/package.json +4 -3
  510. package/dist/init/cursor.d.ts +0 -24
  511. package/dist/init/cursor.js +0 -80
  512. package/dist/session/session-readiness.d.ts +0 -34
  513. package/dist/session/session-readiness.js +0 -36
@@ -13,61 +13,99 @@ async function queryFirst(session, q) {
13
13
  return undefined;
14
14
  }
15
15
  /**
16
- * A note attached to `{ found: false, exhausted: true }` only when the failure is
17
- * actually informative: the document itself had nothing to scroll (scrolled:false with
18
- * no explicit container), so the target is likely inside a list with its own scroll
19
- * container. Reaching the genuine end of a scrolled container stays quiet.
16
+ * A note attached to `{ found: false, exhausted: true }` only when the failure is actually
17
+ * informative: nothing in the container ever moved (so the "list" being searched was never
18
+ * scrollable and the target is likely inside one with its own scroll container). Reaching the
19
+ * genuine ends of a container that DID scroll stays quiet — that is exhaustion, not confusion.
20
20
  */
21
- function exhaustNote(q, data) {
21
+ function exhaustNote(q, data, nothingEverScrolled) {
22
22
  if (q.container !== undefined)
23
23
  return {};
24
- if (false !== data['scrolled'])
24
+ if (true === data['scrolled'] || !nothingEverScrolled)
25
25
  return {};
26
26
  return {
27
27
  note: "The document did not scroll — the target may be inside a list with its own scroll container. Pass that container's ref as `container`.",
28
28
  };
29
29
  }
30
+ /** One viewport-height step upward when the last result carried no clientHeight to size it by. */
31
+ const UP_STEP_FALLBACK_PX = 400;
30
32
  /**
31
- * Reveal an element that a windowed/virtualized list has not mounted yet. Queries
32
- * once (it may already be visible), then scrolls the container ~one viewport at a time, re-querying
33
- * after each, until the element appears, the list ends, or the maxScrolls budget is spent. Pure
34
- * orchestration over the session command seam — fully unit-testable with a fake.
33
+ * Size the upward step off the container the SCROLL result just described, falling back to a
34
+ * plain viewport height when the result did not carry one.
35
+ */
36
+ function upStepPx(last) {
37
+ const h = last['clientHeight'];
38
+ return 'number' === typeof h && h > 0 ? h : UP_STEP_FALLBACK_PX;
39
+ }
40
+ /**
41
+ * Reveal an element that a windowed/virtualized list has not mounted yet. Queries once (it may
42
+ * already be visible), optionally jumps straight to a bisection estimate, then walks the container
43
+ * a viewport at a time re-querying after each step. Downward first; when the bottom is reached the
44
+ * walk TURNS AROUND and continues upward, because a list that is already scrolled to its end keeps
45
+ * its earlier rows mounted above the viewport and a downward-only search answered "exhausted" on
46
+ * its first step (#505). `exhausted` therefore means BOTH directions are spent, not "the bottom is
47
+ * here". Pure orchestration over the session command seam — fully unit-testable with a fake.
35
48
  */
36
49
  export async function scrollToFind(session, q, opts = {}) {
37
50
  const max = opts.maxScrolls ?? SCROLL_FIND_DEFAULTS.MAX_SCROLLS;
38
51
  const first = await queryFirst(session, q);
39
52
  if (first !== undefined)
40
53
  return { found: true, element: first, scrolls: 0, exhausted: false };
41
- // Bisection: if the caller knows the target index and list size, jump to the estimated offset
42
- // in one scroll command rather than stepping a viewport at a time. Then refine linearly.
54
+ const baseArgs = () => q.container !== undefined ? { ref: q.container } : {};
43
55
  let scrolls = 0;
56
+ let last = {};
57
+ // Bisection: if the caller knows the target index and list size, jump to the estimated offset in
58
+ // one scroll command rather than stepping a viewport at a time. The estimate can land on either
59
+ // side of the target, so refinement below still searches both directions from wherever this put us.
44
60
  if (q.targetIndex !== undefined && q.totalCount !== undefined && q.totalCount > 1) {
45
61
  const fraction = Math.min(1, Math.max(0, q.targetIndex / q.totalCount));
46
- const sr = await session.command(ReticleCommand.SCROLL, {
47
- ...(q.container !== undefined ? { ref: q.container } : {}),
48
- fraction,
49
- });
62
+ const sr = await session.command(ReticleCommand.SCROLL, { ...baseArgs(), fraction });
50
63
  scrolls += 1;
64
+ last = asRecord(sr.result);
51
65
  const hit = await queryFirst(session, q);
52
66
  if (hit !== undefined)
53
67
  return { found: true, element: hit, scrolls, exhausted: false };
54
- // Fall through to linear refinement from current position (already near the target).
68
+ }
69
+ let downwardSpent = false;
70
+ let everScrolled = false;
71
+ // The downward pass. Spending its whole budget without reaching the end means rows may remain
72
+ // further down, so that answer is `exhausted:false` and the upward pass never runs.
73
+ for (let i = 0; i < max && !downwardSpent; i += 1) {
74
+ const sr = await session.command(ReticleCommand.SCROLL, baseArgs());
75
+ scrolls += 1;
55
76
  const data = asRecord(sr.result);
56
- if (true === data['atEnd'] || false === data['scrolled']) {
57
- return { found: false, scrolls, exhausted: true, ...exhaustNote(q, data) };
58
- }
77
+ last = data;
78
+ if (true === data['scrolled'])
79
+ everScrolled = true;
80
+ const hit = await queryFirst(session, q);
81
+ if (hit !== undefined)
82
+ return { found: true, element: hit, scrolls, exhausted: false };
83
+ // Reached the bottom, or the container would not move downward — turn around before giving up.
84
+ if (true === data['atEnd'] || true !== data['scrolled'])
85
+ downwardSpent = true;
86
+ }
87
+ // If the downward pass spent its budget it never saw the end, so there is nothing to turn around
88
+ // for; report honestly rather than doubling a call whose caller asked for `max` steps.
89
+ if (!downwardSpent) {
90
+ return { found: false, scrolls, exhausted: false };
59
91
  }
92
+ // The upward pass gets the caller's budget again, not what is left of it: a list at the bottom of
93
+ // 14k px needs ~90 scrolls to reach its end and ~90 more to get back, so sharing one budget would
94
+ // strand the search halfway and answer `exhausted:false` about a list both of whose ends are known.
60
95
  for (let i = 0; i < max; i += 1) {
61
- const sr = await session.command(ReticleCommand.SCROLL, q.container !== undefined ? { ref: q.container } : {});
96
+ const sr = await session.command(ReticleCommand.SCROLL, { ...baseArgs(), dy: -upStepPx(last) });
62
97
  scrolls += 1;
63
98
  const data = asRecord(sr.result);
99
+ last = data;
100
+ if (true === data['scrolled'])
101
+ everScrolled = true;
64
102
  const hit = await queryFirst(session, q);
65
103
  if (hit !== undefined)
66
104
  return { found: true, element: hit, scrolls, exhausted: false };
67
- // Reached the bottom or the container would not move — no more rows to reveal.
68
- if (true === data['atEnd'] || false === data['scrolled']) {
69
- return { found: false, scrolls, exhausted: true, ...exhaustNote(q, data) };
105
+ // The top refusing to move means both directions are spent.
106
+ if (true !== data['scrolled']) {
107
+ return { found: false, scrolls, exhausted: true, ...exhaustNote(q, data, !everScrolled) };
70
108
  }
71
109
  }
72
- return { found: false, scrolls, exhausted: false }; // spent the budget; more may lie further down
110
+ return { found: false, scrolls, exhausted: false }; // spent the upward budget; more may lie above
73
111
  }
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import { QueryBy } from '@reticlehq/core';
3
3
  import { ReticleTool } from '../tools/tool-names.js';
4
4
  import { asNumber, asString } from '../tools/tools-helpers.js';
5
+ import { listIndexSchema, scrollCountSchema } from '../tools/numeric-bounds.js';
5
6
  import { scrollToFind } from './scroll-find.js';
6
7
  /**
7
8
  * reticle_scroll_to — the windowed/virtualized-list escape hatch. A plain reticle_query
@@ -30,16 +31,13 @@ export const SCROLL_TOOLS = [
30
31
  .string()
31
32
  .optional()
32
33
  .describe('Element ref for the scrollable container. Omit to scroll the document.'),
33
- maxScrolls: z
34
- .number()
34
+ maxScrolls: scrollCountSchema
35
35
  .optional()
36
36
  .describe('Maximum number of scroll steps before giving up. Default: 20.'),
37
- targetIndex: z
38
- .number()
37
+ targetIndex: listIndexSchema
39
38
  .optional()
40
39
  .describe('Known row index of the target in the list. Combine with totalCount for bisection — jumps directly to the estimated offset.'),
41
- totalCount: z
42
- .number()
40
+ totalCount: listIndexSchema
43
41
  .optional()
44
42
  .describe('Total item count in the virtualized list. Required for bisection with targetIndex.'),
45
43
  sessionId: z
@@ -2,10 +2,11 @@ import { z } from 'zod';
2
2
  import { CDP_NO_PROVIDER_REASON, CDP_NO_PROVIDER_RECOMMENDATION } from '@reticlehq/core';
3
3
  import { ReticleTool } from '../tools/tool-names.js';
4
4
  import { sessionIdShape } from '../tools/tool-kit.js';
5
+ import { MAX_VIEWPORT_PX, MIN_VIEWPORT_PX, viewportPxSchema } from '../tools/numeric-bounds.js';
5
6
  import { asString } from '../tools/tools-helpers.js';
6
7
  /** Bounds so a viewport request stays sane (and a typo can't ask for a 1px or 100k-px window). */
7
- const MIN_DIM = 64;
8
- const MAX_DIM = 10000;
8
+ const MIN_DIM = MIN_VIEWPORT_PX;
9
+ const MAX_DIM = MAX_VIEWPORT_PX;
9
10
  function viewportProvider(deps) {
10
11
  const p = deps.realInput;
11
12
  return p !== undefined && 'function' === typeof p.setViewport
@@ -20,8 +21,8 @@ export const VIEWPORT_TOOLS = [
20
21
  'reticle_visual_diff `masks` and a frozen clock (reticle_clock). Set it once before reticle_screenshot / ' +
21
22
  'reticle_visual_diff. Returns { applied, width, height } or the no-provider recommendation.',
22
23
  inputSchema: {
23
- width: z.number().int().describe('Viewport width in CSS px (e.g. 1280).'),
24
- height: z.number().int().describe('Viewport height in CSS px (e.g. 800).'),
24
+ width: viewportPxSchema.describe('Viewport width in CSS px (e.g. 1280).'),
25
+ height: viewportPxSchema.describe('Viewport height in CSS px (e.g. 800).'),
25
26
  ...sessionIdShape,
26
27
  },
27
28
  outputSchema: {
@@ -0,0 +1,37 @@
1
+ import type { ToolDeps } from '../tools/tool-kit.js';
2
+ /**
3
+ * The ledger id a piece of inline prose is declared under.
4
+ *
5
+ * Derived from the statement rather than minted, so the same sentence declared on five verdicts is
6
+ * one row rather than five, and a DIFFERENT sentence is a different row rather than a false
7
+ * amendment of the first. The slug is there because the id is what an agent later types to reference
8
+ * the intent, and a bare digest is unreadable; the digest is there because the slug is truncated and
9
+ * two long statements sharing a prefix must not share a row.
10
+ */
11
+ export declare function inlineIntentId(statement: string): string;
12
+ /** What `provenBy` records: which tool drew the verdict, and when. */
13
+ export declare function inlineVerdictId(tool: string, at: number): string;
14
+ export declare function surfaceForInlineIntent(url: string | undefined, flow: string | undefined,
15
+ /** `file` or `file:line` for the element the verdict acted on, when it had one. */
16
+ sourceLabel?: string): {
17
+ route?: string;
18
+ flow?: string;
19
+ files?: string[];
20
+ } | undefined;
21
+ export declare function linkInlineIntent(deps: ToolDeps, sessionId: string | undefined, intent: string | undefined,
22
+ /** The predicate this verdict will evaluate, or undefined when it proves nothing (a bare settle). */
23
+ binding: unknown): Promise<string | undefined>;
24
+ /**
25
+ * Record that this verdict proved the intent. Call only for a verdict that actually proved something.
26
+ *
27
+ * The ledger's own rule does the rest: `dischargeIntent` refuses an intent with no binding, so an
28
+ * intent declared inline on a wait that asserted no consequence stays open and honest instead of
29
+ * collecting a proof nothing earned.
30
+ */
31
+ export declare function dischargeInlineIntent(deps: ToolDeps, sessionId: string | undefined, id: string | undefined, proof: {
32
+ verdictId: string;
33
+ grade: string;
34
+ at: number;
35
+ },
36
+ /** `file:line` of the element this verdict acted on, when there was one. */
37
+ sourceLabel?: string): Promise<void>;
@@ -0,0 +1,193 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { IntentStore } from './intent-store.js';
3
+ import { sessionRoot } from '../project/session-root.js';
4
+ /**
5
+ * Intent declared INLINE, on the tool that is already drawing the verdict.
6
+ *
7
+ * `reticle_intent` is on the extended surface, so declaring intent costs an agent a discovery, a
8
+ * decision and a round trip before it has done anything — three places the capture silently fails,
9
+ * and it does. `reticle_act_and_wait` and `reticle_assert` are in every agent's tool list already,
10
+ * so one optional argument there makes the declaration free and discoverable by construction.
11
+ *
12
+ * This is a shortcut into the EXISTING ledger, never a second one. It writes `.reticle/intent.json`
13
+ * through `IntentStore` exactly as `reticle_intent { action: "declare" }` does, so a reviewer reads
14
+ * one file in one vocabulary and cannot tell from the row which door the intent came in by. The
15
+ * pattern is `flows/flow-intent.ts`, which does the same job for a saved flow.
16
+ */
17
+ /** Namespaced like `flow:`, so an inline id never collides with one an agent chose by hand. */
18
+ const INLINE_INTENT_ID_PREFIX = 'inline:';
19
+ const ID_SEPARATOR = ':';
20
+ const SLUG_SEPARATOR = '-';
21
+ const SLUG_MAX = 48;
22
+ const HASH_LENGTH = 8;
23
+ const HASH_ALGORITHM = 'sha1';
24
+ const NON_SLUG = /[^a-z0-9]+/g;
25
+ const SLUG_EDGES = /^-+|-+$/g;
26
+ /**
27
+ * The ledger id a piece of inline prose is declared under.
28
+ *
29
+ * Derived from the statement rather than minted, so the same sentence declared on five verdicts is
30
+ * one row rather than five, and a DIFFERENT sentence is a different row rather than a false
31
+ * amendment of the first. The slug is there because the id is what an agent later types to reference
32
+ * the intent, and a bare digest is unreadable; the digest is there because the slug is truncated and
33
+ * two long statements sharing a prefix must not share a row.
34
+ */
35
+ export function inlineIntentId(statement) {
36
+ const slug = statement
37
+ .toLowerCase()
38
+ .replace(NON_SLUG, SLUG_SEPARATOR)
39
+ .replace(SLUG_EDGES, '')
40
+ .slice(0, SLUG_MAX)
41
+ .replace(SLUG_EDGES, '');
42
+ const digest = createHash(HASH_ALGORITHM).update(statement).digest('hex').slice(0, HASH_LENGTH);
43
+ return `${INLINE_INTENT_ID_PREFIX}${slug}${SLUG_SEPARATOR}${digest}`;
44
+ }
45
+ /** What `provenBy` records: which tool drew the verdict, and when. */
46
+ export function inlineVerdictId(tool, at) {
47
+ return `${tool}${ID_SEPARATOR}${String(at)}`;
48
+ }
49
+ /**
50
+ * Put an inline intent in the ledger and attach the predicate about to be evaluated. Returns the id
51
+ * the verdict may later discharge, or undefined when there was no intent to record.
52
+ *
53
+ * `intent` carries EITHER prose OR the id of an intent already in the ledger. One field rather than
54
+ * two because the surface is the scarce thing here and the two cases are told apart by the ledger
55
+ * itself: a string that names an existing row is a reference, anything else is prose. A statement
56
+ * that happens to equal an existing id is the same intent said twice, so pointing at the row is the
57
+ * right answer there too, not a collision.
58
+ *
59
+ * A referenced intent is never re-declared, which would overwrite the agent's own words with an id,
60
+ * and its binding is left alone when it already has one — a predicate bound deliberately through
61
+ * `reticle_intent { action: "bind" }` is a stronger statement than whatever this one call asserts,
62
+ * and quietly replacing it is exactly the narrowing the ledger exists to make visible.
63
+ *
64
+ * Best-effort by construction: a ledger that cannot be written is a small problem, and a verdict
65
+ * that fails to return because of one is a large problem.
66
+ */
67
+ /**
68
+ * The surface an inline intent was captured on, so the store can FILE it.
69
+ *
70
+ * Measured on a real corpus: 167 of 173 things a project knew landed in `unsorted`, because
71
+ * `act_and_wait({ intent })` declared a statement and nothing else. The subject ladder had no flow,
72
+ * no route and no explicit subject to work from, so every record fell to the bucket of last resort
73
+ * — and a coverage map that is one pile with six labels tells a manager the team knows nothing,
74
+ * when the truth is that it knows a great deal and none of it is filed.
75
+ *
76
+ * The ROUTE is what makes this work at all, because it is always available: an agent asserting
77
+ * something is always somewhere. A flow name is better when there is one, and `subjectFor` already
78
+ * prefers it — this only has to supply both and let that ladder decide.
79
+ *
80
+ * Query and hash are dropped. `/issues?category=severe` and `/issues` are the same subject seen
81
+ * twice; keeping the query would shard one area of the product across every filter anybody used.
82
+ *
83
+ * Returns undefined rather than an empty object when there is nothing to record. A surface that
84
+ * says nothing reads as a capture that looked for a location and found none, rather than one that
85
+ * never had one — and the second is the truth.
86
+ */
87
+ /**
88
+ * Strip a trailing `:line` from a `file:line` label.
89
+ *
90
+ * Anchored to the END and to digits, because a Windows path carries its own colon: splitting on the
91
+ * first one turns `C:\\app\\src\\cart.tsx:12` into `C`, which is a path to nothing and would be
92
+ * filed as though it were real.
93
+ */
94
+ const SOURCE_LINE_SUFFIX = /:\d+$/;
95
+ export function surfaceForInlineIntent(url, flow,
96
+ /** `file` or `file:line` for the element the verdict acted on, when it had one. */
97
+ sourceLabel) {
98
+ let route;
99
+ if (url !== undefined) {
100
+ try {
101
+ const path = new URL(url).pathname;
102
+ // A bare origin has no path worth filing under: `/` would put every record in one bucket,
103
+ // which is the problem this exists to fix rather than a solution to it.
104
+ if (path.length > 1)
105
+ route = path;
106
+ }
107
+ catch {
108
+ route = undefined;
109
+ }
110
+ }
111
+ /*
112
+ * The file, without its line.
113
+ *
114
+ * A record is about a FILE; the line is where one verdict happened to touch it, and keeping it
115
+ * would make the same rule look like a different one every time the file was edited above it.
116
+ */
117
+ const file = sourceLabel === undefined || 0 === sourceLabel.length
118
+ ? undefined
119
+ : sourceLabel.replace(SOURCE_LINE_SUFFIX, '');
120
+ if (route === undefined && flow === undefined && file === undefined)
121
+ return undefined;
122
+ return {
123
+ ...(route === undefined ? {} : { route }),
124
+ ...(flow === undefined ? {} : { flow }),
125
+ ...(file === undefined ? {} : { files: [file] }),
126
+ };
127
+ }
128
+ /**
129
+ * The session's live URL, or undefined when no session can be named.
130
+ *
131
+ * `sessions.resolve` throws for every "cannot tell which tab" case, and none of them is a reason to
132
+ * fail the caller's verdict — a missing surface costs a filing, not a result.
133
+ */
134
+ function sessionUrl(deps, sessionId) {
135
+ try {
136
+ return deps.sessions.resolve(sessionId).url;
137
+ }
138
+ catch {
139
+ return undefined;
140
+ }
141
+ }
142
+ export async function linkInlineIntent(deps, sessionId, intent,
143
+ /** The predicate this verdict will evaluate, or undefined when it proves nothing (a bare settle). */
144
+ binding) {
145
+ if (intent === undefined || 0 === intent.trim().length)
146
+ return undefined;
147
+ try {
148
+ const store = new IntentStore(deps.fs, sessionRoot(deps, sessionId), { now: deps.now });
149
+ const existing = (await store.read()).find((row) => row.id === intent);
150
+ const id = existing?.id ?? inlineIntentId(intent);
151
+ // Declared WITHOUT a surface on purpose: see dischargeInlineIntent for why the route that
152
+ // describes this record only exists after the action it is about.
153
+ if (existing === undefined)
154
+ await store.declare([{ id, statement: intent }]);
155
+ if (binding !== undefined && existing?.binding === undefined)
156
+ await store.bind(id, binding);
157
+ return id;
158
+ }
159
+ catch {
160
+ return undefined;
161
+ }
162
+ }
163
+ /**
164
+ * Record that this verdict proved the intent. Call only for a verdict that actually proved something.
165
+ *
166
+ * The ledger's own rule does the rest: `dischargeIntent` refuses an intent with no binding, so an
167
+ * intent declared inline on a wait that asserted no consequence stays open and honest instead of
168
+ * collecting a proof nothing earned.
169
+ */
170
+ export async function dischargeInlineIntent(deps, sessionId, id, proof,
171
+ /** `file:line` of the element this verdict acted on, when there was one. */
172
+ sourceLabel) {
173
+ if (id === undefined)
174
+ return;
175
+ try {
176
+ const store = new IntentStore(deps.fs, sessionRoot(deps, sessionId), { now: deps.now });
177
+ /*
178
+ * File it HERE, not at declaration.
179
+ *
180
+ * The intent is declared before the action so a verdict can see it open, and at that instant the
181
+ * agent is still on the page it is leaving — clicking through to /issues from / would have filed
182
+ * the record under the page it left, or under nothing at all. The route that describes what the
183
+ * intent is about is the one that exists once the consequence has landed.
184
+ */
185
+ const surface = surfaceForInlineIntent(sessionUrl(deps, sessionId), undefined, sourceLabel);
186
+ if (surface !== undefined)
187
+ await store.place(id, surface);
188
+ await store.discharge(id, proof);
189
+ }
190
+ catch {
191
+ // The verdict already stood. The proof is simply not recorded — see dischargeFlowIntent.
192
+ }
193
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Reading and writing the sharded intent store, and migrating the flat one into it.
3
+ *
4
+ * The operations an agent actually performs, and what each is allowed to cost:
5
+ *
6
+ * index() every id + one line, always affordable — the session-start read
7
+ * subject(name) the full records for one subject, when it is about to work on that
8
+ * get(id) one record, via the index rather than by scanning shards
9
+ * record()/update() a write that touches ONE shard file plus the index
10
+ *
11
+ * The last one is the reason for the whole design. Under the flat file, changing one intent rewrote
12
+ * all 141, so two sessions touching unrelated subjects raced each other and the loser's write
13
+ * vanished. Sharded, they only collide when they genuinely edit the same subject.
14
+ *
15
+ * ## Migration is automatic, and it does not delete
16
+ *
17
+ * `.reticle/intent.json` is read on every load and folded in when the sharded copy has nothing for
18
+ * an id. That makes the move invisible: an agent on the new store sees old intents immediately, and
19
+ * an older build still reading the flat file keeps working, because nothing removes it. Deleting it
20
+ * is a separate decision somebody can take once they believe the migration.
21
+ */
22
+ import { type Intent } from '@reticlehq/core';
23
+ import type { FileSystemPort } from '../project/fs-port.js';
24
+ import { IntentStatus, type IntentIndex, type IntentRecord } from './intent-shard.js';
25
+ export interface Clock {
26
+ now: () => number;
27
+ }
28
+ /** What a caller supplies to write an intent. Everything optional is genuinely optional. */
29
+ export interface IntentInput {
30
+ id: string;
31
+ statement: string;
32
+ subject?: string | undefined;
33
+ why?: string | undefined;
34
+ source?: string | undefined;
35
+ status?: IntentStatus | undefined;
36
+ surface?: Intent['surface'];
37
+ binding?: unknown;
38
+ }
39
+ export declare class IntentShardStore {
40
+ #private;
41
+ constructor(fs: FileSystemPort, root: string, clock: Clock);
42
+ /**
43
+ * The cheap read: one line per intent, and the call an agent makes at the start of a session.
44
+ *
45
+ * Includes anything still only in the legacy flat file, so the very first call on an unmigrated
46
+ * project already answers completely — which is what makes the migration invisible rather than a
47
+ * step somebody has to remember to run.
48
+ */
49
+ index(): Promise<IntentIndex>;
50
+ /** Every record for one subject — the working read, once an agent knows what it is touching. */
51
+ subject(name: string): Promise<IntentRecord[]>;
52
+ /** One record by id, or null. */
53
+ get(id: string): Promise<IntentRecord | null>;
54
+ /**
55
+ * Write one intent, creating or updating it.
56
+ *
57
+ * Merges onto whatever is already stored, so a caller adding a `why` to a migrated record does not
58
+ * have to restate its binding — and cannot silently drop it by omission, which is the failure mode
59
+ * that makes agents afraid to touch a store.
60
+ */
61
+ record(input: IntentInput): Promise<IntentRecord>;
62
+ /**
63
+ * Fold the flat file into shards on disk, so the move is visible in a diff rather than implicit.
64
+ *
65
+ * Idempotent, and it does not delete `.reticle/intent.json`: an older build still reads it, and a
66
+ * migration that removes its own source cannot be checked afterwards.
67
+ */
68
+ migrate(): Promise<{
69
+ migrated: number;
70
+ subjects: string[];
71
+ }>;
72
+ }