@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
@@ -0,0 +1,233 @@
1
+ import { IntentState, JournalVerdictEffectSchema, RUN_ESTABLISHED_CAP, } from '@reticlehq/core';
2
+ import { subjectOf } from '../runs/run-context.js';
3
+ import { ReticleTool } from '../tools/tool-names.js';
4
+ /**
5
+ * Were `reticle_context` and `reticle_intent` used at all — and did using them change anything?
6
+ *
7
+ * Both shipped with the same pre-registered disproof: *if agents never call it, cut it*. Nothing
8
+ * recorded whether they were called, so the disproof could never be run, and a feature whose
9
+ * disproof cannot be run is one nobody can kill. This is the smallest thing that makes the question
10
+ * answerable, per session, locally — an instrument, not a scold. The numbers exist so a FEATURE can
11
+ * be removed, never so an agent can be blamed for how it drove.
12
+ *
13
+ * ## Fold first, record only what nothing records
14
+ *
15
+ * Three of the four questions are already answerable from what is on disk. The intent ledger says
16
+ * what was declared; the journal says what was driven and what verdicts were drawn. Those are folded
17
+ * here and never re-stored, for the reason `run-context.ts` gives at length: two ways to compute one
18
+ * number is worse than one way.
19
+ *
20
+ * The journal does NOT record read-only calls — it holds actions and verdicts only. So "was
21
+ * `reticle_context` called" has no ledger to fold, and that one thing is recorded, in memory, on the
22
+ * session, by `CaptureLedger`. It is the whole of the new state.
23
+ *
24
+ * ## Absent is not zero
25
+ *
26
+ * A session this daemon has recorded nothing for — journaling off, a session adopted after the fact
27
+ * — reports `observed: false` and states no counts at all. It has not established that the features
28
+ * went unused; it has established that it was not watching. This repo has shipped the other bug
29
+ * twice.
30
+ */
31
+ /**
32
+ * The calls this instrument needs and the journal does not keep.
33
+ *
34
+ * Deliberately a short explicit list rather than "everything that is not an action". Under-inclusion
35
+ * is the safe direction: a read tool missing from here lowers the hit side and the miss side by the
36
+ * same call, so the comparison the disproof turns on stays fair. Over-inclusion is not — a lifecycle
37
+ * or disk-side tool landing in here would be counted as a "read" the agent made of the app, which it
38
+ * was not.
39
+ */
40
+ export const CAPTURED_TOOLS = new Set([
41
+ ReticleTool.CONTEXT,
42
+ ReticleTool.SNAPSHOT,
43
+ ReticleTool.QUERY,
44
+ ReticleTool.INSPECT,
45
+ ReticleTool.STATE,
46
+ ReticleTool.STORAGE,
47
+ ReticleTool.NETWORK,
48
+ ReticleTool.CONSOLE,
49
+ ReticleTool.OBSERVE,
50
+ ReticleTool.EXPLORE,
51
+ ]);
52
+ /**
53
+ * How many calls the ledger holds before it starts dropping the oldest.
54
+ *
55
+ * A verification loop is 50–200 calls, so this is not reached in normal use; it exists so a runaway
56
+ * session cannot grow a per-session array without bound. When it IS reached the report says
57
+ * `truncated` rather than presenting a short count as a complete one.
58
+ */
59
+ const CAPTURE_CAP = 2000;
60
+ /**
61
+ * The one thing that is stored rather than folded: the read-only calls the journal never sees.
62
+ *
63
+ * In memory and per session, the same shape as `GapLedger`. It is not durable on purpose — the
64
+ * question is about one run, and a second file on disk would be a second thing to reconcile with the
65
+ * journal.
66
+ */
67
+ export class CaptureLedger {
68
+ #calls = [];
69
+ #dropped = 0;
70
+ #tools = new Map();
71
+ /**
72
+ * One call, under the name of the tool that RAN — every tool, not just the read set above.
73
+ *
74
+ * Separate from `note` because it answers a different question and is bounded differently: this
75
+ * map cannot outgrow the tool table however long the session runs, so it needs no cap and never
76
+ * has to report a floor. See honesty/tool-hit-rate.ts for what it feeds.
77
+ */
78
+ noteTool(tool) {
79
+ this.#tools.set(tool, (this.#tools.get(tool) ?? 0) + 1);
80
+ }
81
+ /** How many times each tool was called this session, in first-call order. */
82
+ toolCalls() {
83
+ return this.#tools;
84
+ }
85
+ note(call) {
86
+ this.#calls.push(call);
87
+ if (this.#calls.length > CAPTURE_CAP) {
88
+ this.#calls.shift();
89
+ this.#dropped += 1;
90
+ }
91
+ }
92
+ calls() {
93
+ return this.#calls;
94
+ }
95
+ /** How many calls fell off the front. Non-zero means the counts below are a floor. */
96
+ get dropped() {
97
+ return this.#dropped;
98
+ }
99
+ }
100
+ /**
101
+ * Record one captured call against the session that made it.
102
+ *
103
+ * Tolerates a session that carries no ledger, exactly as `noteSessionGaps` does and for the same
104
+ * reason: `Session` is satisfied structurally, and many specs — plus any consumer embedding this
105
+ * engine — build one as an object literal. Such a session is a real caller that simply keeps no
106
+ * ledger, and an instrument must never be the reason a tool call throws. Its calls are then absent
107
+ * rather than counted as zero, which is what `observed: false` exists to say.
108
+ */
109
+ export function noteCapturedCall(session, call) {
110
+ session.capture?.note({ ...call, afterActions: session.actionCount ?? 0 });
111
+ }
112
+ /**
113
+ * Count one call against the session that made it, whichever tool it was.
114
+ *
115
+ * Tolerant of a ledger-less session for exactly the reason `noteCapturedCall` is: its calls are then
116
+ * absent rather than counted as zero, which is what `observed: false` exists to say.
117
+ */
118
+ export function noteToolDispatched(session, tool) {
119
+ session.capture?.noteTool(tool);
120
+ }
121
+ /**
122
+ * Every point in the run at which each subject became an established fact.
123
+ *
124
+ * "Established" is not a second definition: it is the rule `establishedFromJournal` folds by — an
125
+ * action with an observed settle outcome, filed under the ref or target it named — and `subjectOf`
126
+ * is imported from there rather than rewritten, so the two cannot drift apart.
127
+ */
128
+ function establishedIndexes(actions) {
129
+ const out = new Map();
130
+ actions.forEach((action, index) => {
131
+ if (undefined === action.settled)
132
+ return;
133
+ const key = subjectOf(action);
134
+ if (undefined === key)
135
+ return;
136
+ const at = out.get(key);
137
+ if (at === undefined)
138
+ out.set(key, [index]);
139
+ else
140
+ at.push(index);
141
+ });
142
+ return out;
143
+ }
144
+ /** Was this subject already an established fact by the time a call at `afterActions` was made? */
145
+ function wasEstablished(index, subject, afterActions) {
146
+ if (undefined === subject)
147
+ return false;
148
+ const at = index.get(subject);
149
+ if (at === undefined)
150
+ return false;
151
+ // Action `i` has been dispatched once the counter reads `i + 1`, so `i + 1 <= afterActions` is
152
+ // "already true when the call was made" — which is what keeps a FIRST look off this count. The
153
+ // lower bound is the run context's own window: a fact that has scrolled past RUN_ESTABLISHED_CAP
154
+ // is not something `reticle_context` would have handed back, so re-reading it is not a re-fetch.
155
+ return at.some((i) => i + 1 <= afterActions && i + 1 > afterActions - RUN_ESTABLISHED_CAP);
156
+ }
157
+ /**
158
+ * Did calling `reticle_context` change what happened next?
159
+ *
160
+ * The mechanical proxy, stated so it can be argued with: look at the very next recorded call. If an
161
+ * action was dispatched before it — the action counter moved — the agent ACTED, and the context
162
+ * plausibly helped. If instead the next call is a read of a subject the context had just supplied,
163
+ * the agent re-fetched what it had been handed, and the context did not help.
164
+ *
165
+ * ## What this proxy CANNOT see
166
+ *
167
+ * - Whether the agent READ the response, reasoned over it, or wrote a better answer from it. There
168
+ * is no signal for that anywhere, and this must not be mistaken for one.
169
+ * - Anything that happens between two Reticle calls: source edits, calls to other MCP servers, a
170
+ * turn ending, a human interrupting. A long gap is invisible and reads as adjacency.
171
+ * - Reads that name no ref or target (a whole-page snapshot). They land in `readOther`, never in
172
+ * `refetched`, so a snapshot that duplicated the context is under-counted, not over-counted.
173
+ * - Causation. `acted` is proximity in a call sequence, nothing more. An agent that would have acted
174
+ * anyway is indistinguishable from one the context unblocked.
175
+ * - `reticle_context` called with no session connected — it answers there by design, and there is no
176
+ * per-session ledger to record it against, so those calls are missing entirely.
177
+ * - Two agents driving one session. Their calls interleave into one sequence and "next call" stops
178
+ * meaning "next thing that agent did".
179
+ */
180
+ export function foldFeatureCapture(input) {
181
+ const { calls, actions, intents } = input;
182
+ if (0 === calls.length && 0 === actions.length)
183
+ return { observed: false };
184
+ const established = establishedIndexes(actions);
185
+ const isRefetch = (call) => ReticleTool.CONTEXT !== call.tool &&
186
+ wasEstablished(established, call.subject, call.afterActions);
187
+ let acted = 0;
188
+ let refetched = 0;
189
+ let readOther = 0;
190
+ let nothingAfter = 0;
191
+ const atSteps = [];
192
+ calls.forEach((call, i) => {
193
+ if (ReticleTool.CONTEXT !== call.tool)
194
+ return;
195
+ atSteps.push(call.afterActions);
196
+ const next = calls[i + 1];
197
+ if (next === undefined) {
198
+ if (input.finalActions > call.afterActions)
199
+ acted += 1;
200
+ else
201
+ nothingAfter += 1;
202
+ return;
203
+ }
204
+ if (next.afterActions > call.afterActions)
205
+ acted += 1;
206
+ else if (isRefetch(next))
207
+ refetched += 1;
208
+ else
209
+ readOther += 1;
210
+ });
211
+ // A verdict is any journal action carrying the bounded verdict effect both verification tools
212
+ // write. Parsed with core's own schema rather than a local shape, for the usual reason.
213
+ const verdicts = actions.filter((a) => JournalVerdictEffectSchema.safeParse(a.effect).success);
214
+ return {
215
+ observed: true,
216
+ ...(input.dropped > 0 ? { truncated: true } : {}),
217
+ context: { calls: atSteps.length, atSteps, acted, refetched, readOther, nothingAfter },
218
+ intents: {
219
+ declared: intents.length,
220
+ open: intents.filter((it) => IntentState.PROVED !== it.state).length,
221
+ },
222
+ missed: {
223
+ // The coarse question, asked honestly. Nothing binds a verdict to a specific intent outside
224
+ // flow replay, so "covered" cannot mean "this intent proves this verdict" without inventing a
225
+ // link Reticle cannot observe — the same reason `undeclared-change.ts` asks only whether
226
+ // anything at all was declared. So this counts verdicts drawn against an EMPTY ledger, and
227
+ // stops at zero once anything is declared. That zero is not a claim that every verdict was
228
+ // covered; it is the end of what this can see.
229
+ verdictsWithNoIntentDeclared: 0 === intents.length ? verdicts.length : 0,
230
+ refetchedEstablished: calls.filter(isRefetch).length,
231
+ },
232
+ };
233
+ }
@@ -22,6 +22,15 @@ interface HonestyInputs {
22
22
  envelopeSamples?: number;
23
23
  coveragePct?: number;
24
24
  coveragePartial?: boolean;
25
+ /**
26
+ * The sentence naming WHAT went unobserved, from `buildCoverageStatement`.
27
+ *
28
+ * Separate from `blindSpots`, which carries only the IMPEACHING notes — the ones that downgrade a
29
+ * verdict. A bounding spot does not impeach anything, so it never entered that list, and the flag
30
+ * that survived (`partial: true`) is unanswerable on its own: the verdict prose tells the reader
31
+ * to see `coverage` for what went unobserved, and there was nothing there to see.
32
+ */
33
+ coverageNote?: string;
25
34
  truncated?: boolean;
26
35
  blindSpots?: readonly string[];
27
36
  /**
@@ -55,6 +64,7 @@ export interface HonestyBlock {
55
64
  coverage: {
56
65
  pct?: number;
57
66
  partial: boolean;
67
+ note?: string;
58
68
  };
59
69
  integrity: {
60
70
  clean: boolean;
@@ -76,4 +86,18 @@ export declare function meetsHonestyBar(block: HonestyBlock, bar?: HonestyBar):
76
86
  ok: boolean;
77
87
  reasons: string[];
78
88
  };
89
+ /**
90
+ * The coverage sentence, kept only on a verdict that points at it.
91
+ *
92
+ * Exactly one branch of `decideVerified` promises it — the YES branch says "coverage was PARTIAL —
93
+ * see `coverage` for what went unobserved". UNKNOWN keeps it too, because "I could not tell" is a
94
+ * statement about what went unseen and the note is the answer. A NO has already named a concrete
95
+ * counter-example, nothing directs the reader to `coverage`, and `partial` plus the spot kinds still
96
+ * travel — so the prose there is cost with no question behind it.
97
+ *
98
+ * Measured: emitting it on every verdict regressed the benchmark's verification efficiency by 4.1%,
99
+ * one note per observation. Accuracy outranks tokens, but this buys no accuracy — the promise is
100
+ * kept wherever it is made.
101
+ */
102
+ export declare function honestyForVerdict(verified: string, honesty: HonestyBlock): HonestyBlock;
79
103
  export {};
@@ -5,6 +5,7 @@
5
5
  * block. Harnesses gate on it (e.g. "grade ≥ net AND integrity clean"). This is pure composition +
6
6
  * assessment — all five components already exist individually; wiring it onto results is a later step.
7
7
  */
8
+ import { Verified } from '@reticlehq/core';
8
9
  /** Assertion grade, strongest first — the tier the verdict actually proved. */
9
10
  export const HonestyGrade = {
10
11
  SIGNAL: 'signal',
@@ -45,7 +46,11 @@ export function buildHonestyBlock(inputs) {
45
46
  ...(samples === undefined
46
47
  ? {}
47
48
  : { envelope: { samples, sufficient: samples >= MIN_ENVELOPE_SAMPLES } }),
48
- coverage: { ...(pct === undefined ? {} : { pct }), partial },
49
+ coverage: {
50
+ ...(pct === undefined ? {} : { pct }),
51
+ partial,
52
+ ...(partial && inputs.coverageNote !== undefined ? { note: inputs.coverageNote } : {}),
53
+ },
49
54
  ...(inputs.settled === undefined ? {} : { settled: inputs.settled }),
50
55
  integrity: {
51
56
  clean: 0 === issues.length,
@@ -69,3 +74,22 @@ export function meetsHonestyBar(block, bar = {}) {
69
74
  }
70
75
  return { ok: 0 === reasons.length, reasons };
71
76
  }
77
+ /**
78
+ * The coverage sentence, kept only on a verdict that points at it.
79
+ *
80
+ * Exactly one branch of `decideVerified` promises it — the YES branch says "coverage was PARTIAL —
81
+ * see `coverage` for what went unobserved". UNKNOWN keeps it too, because "I could not tell" is a
82
+ * statement about what went unseen and the note is the answer. A NO has already named a concrete
83
+ * counter-example, nothing directs the reader to `coverage`, and `partial` plus the spot kinds still
84
+ * travel — so the prose there is cost with no question behind it.
85
+ *
86
+ * Measured: emitting it on every verdict regressed the benchmark's verification efficiency by 4.1%,
87
+ * one note per observation. Accuracy outranks tokens, but this buys no accuracy — the promise is
88
+ * kept wherever it is made.
89
+ */
90
+ export function honestyForVerdict(verified, honesty) {
91
+ if (Verified.NO !== verified || honesty.coverage.note === undefined)
92
+ return honesty;
93
+ const { note: _dropped, ...coverage } = honesty.coverage;
94
+ return { ...honesty, coverage };
95
+ }
@@ -29,20 +29,28 @@ export interface ActionInstrumentationFacts {
29
29
  /** True when the outcome was positively PROVED rather than inferred. */
30
30
  proved?: boolean;
31
31
  /**
32
- * Does Reticle know a `file:line` for what this verdict is about?
32
+ * The `file:line` of the element this verdict is about, when the build plugin stamped one.
33
33
  *
34
- * A boolean rather than the source itself, deliberately: the act path holds a `{file, line}` and
35
- * the assert path holds a formatted string it remembered from the last act. The gap does not care
36
- * which — only whether the agent can be pointed at code — and taking the shape would make one of
37
- * the two callers convert for no reason.
34
+ * Was a boolean, on the reasoning that the gap only needed to know WHETHER the agent could be
35
+ * pointed at code. That was true while the only gap reading it was the one that fires when the
36
+ * pointer is missing. It stopped being true the moment a gap wanted to CARRY the pointer: a gap is
37
+ * read back later, out of the ledger, by which time its `ref` is very likely dead, and a boolean
38
+ * cannot be turned back into a location. Both callers already hold this as a formatted string.
38
39
  */
39
- sourceKnown: boolean;
40
+ source?: string | undefined;
40
41
  /** The ref that was driven, for the report. */
41
42
  ref?: string | undefined;
42
43
  /** Did the caller's predicate ask about registered state? */
43
44
  stateAsked: boolean;
44
45
  /** True when the session has no store registered to read. */
45
46
  stateUnwatched: boolean;
47
+ /**
48
+ * Did the app declare any capabilities at all?
49
+ *
50
+ * Undefined means NOT KNOWN, and not-known must never become an accusation — callers that predate
51
+ * this field keep their behaviour rather than start reporting a gap they cannot substantiate.
52
+ */
53
+ hasCapabilities?: boolean | undefined;
46
54
  /** Did the DOM move in this action's window? */
47
55
  domMutated: boolean;
48
56
  /** How many app signals fired in the window. */
@@ -51,5 +59,30 @@ export interface ActionInstrumentationFacts {
51
59
  routeChanged: boolean;
52
60
  /** Did anything signal that route change? */
53
61
  routeSignalFired: boolean;
62
+ /**
63
+ * Did a code change land since the last verdict with nothing declaring what it was for?
64
+ *
65
+ * Arrives already decided, because the decision needs the intent ledger and this stays pure. Its
66
+ * gate — and the reasoning that keeps it from becoming a nag — lives in `isChangeUndeclared`, and
67
+ * that is the only place allowed to answer it.
68
+ */
69
+ changeUndeclared?: boolean;
70
+ /**
71
+ * How many intents the ledger still holds undischarged. Undefined when nothing consulted it.
72
+ *
73
+ * A count rather than a boolean because the message names the number: "one thing still unproved"
74
+ * and "four things still unproved" are different situations for an agent deciding whether it is
75
+ * finished, and a bare flag makes them read the same.
76
+ */
77
+ openIntentCount?: number;
78
+ /**
79
+ * How long the OLDEST open intent has been owed, in ms.
80
+ *
81
+ * A count alone cannot tell "you declared this a minute ago and have not proved it yet" from
82
+ * "this project has owed eighteen things since last week". Only the first is actionable on the
83
+ * result being read, and a number that is always large is a number people learn to skip — which
84
+ * is exactly how an honest gap gets filtered out along with the noise.
85
+ */
86
+ oldestOpenIntentAgeMs?: number;
54
87
  }
55
88
  export declare function gapsForAction(facts: ActionInstrumentationFacts): InstrumentationGap[];
@@ -3,19 +3,102 @@ export function gapsForAction(facts) {
3
3
  const gaps = [];
4
4
  // A red verdict names the control and cannot name the line that renders it. That is the round trip
5
5
  // the agent is about to spend, and the one a build plugin removes permanently.
6
- if (!facts.pass && !facts.sourceKnown) {
6
+ if (!facts.pass && facts.source === undefined) {
7
7
  gaps.push(instrumentationGap(InstrumentationGapKind.NO_SOURCE_MAPPING, 'the control that was driven carries no source mapping', 'this verdict can name the control but not the file and line that render it, so finding the code is a separate search', { ...(facts.ref === undefined ? {} : { ref: facts.ref }) }));
8
8
  }
9
- // Asked about state, and there is no state channel to answer from.
10
- if (facts.stateAsked && facts.stateUnwatched) {
11
- gaps.push(instrumentationGap(InstrumentationGapKind.NO_STORE_REGISTERED, 'no store is registered, and this assertion was about state', 'the assertion could not be answered from the deterministic channel and had to fall back to what the DOM happens to show'));
9
+ // Asked about state, and there is no state channel to answer from. Deliberately UNLOCATED: a store
10
+ // is registered once at app setup, nowhere near the control that was driven, and nothing in this
11
+ // window knows where that setup lives. Borrowing the acted line would send the agent to a file
12
+ // that cannot hold the fix — worse than no pointer, because it costs the trip as well.
13
+ // An app that declared NOTHING is told once, without having to be asked.
14
+ //
15
+ // The rule below fires only when an assertion about state could not be answered, which is correct
16
+ // for that gap and leaves the common case silent: an app whose capabilities file registers
17
+ // nothing drives fine, produces verdicts, and never mentions being under-instrumented — unless
18
+ // the agent happens to ask about state, which an agent with no reason to suspect a problem will
19
+ // not do. The session has known the answer the whole time and no verdict ever consulted it.
20
+ //
21
+ // Reported as the same kind, because it is the same missing thing seen from the other side, and
22
+ // guarded so the two conditions cannot report it twice.
23
+ //
24
+ // BOTH arms require `stateUnwatched`, which is the store registry. Keying the first on
25
+ // `hasCapabilities` alone made this fire on apps whose store IS registered and readable: an app
26
+ // that calls `registerStore()` without `registerCapabilities()` has no declaration and a perfectly
27
+ // good store, and got a gap saying "no state can be read from it" in the same response that
28
+ // carried `reticle_state { found: true }`, a populated `stateDiffs`, and `statePathsChanged`
29
+ // naming the store (#700). A gap contradicted by its own response tells the agent to redo work it
30
+ // has already done, and teaches it to distrust the whole block.
31
+ //
32
+ // The kind is NO_STORE_REGISTERED and every sentence it carries is about stores, so the store
33
+ // registry is what it must be keyed on. An undeclared-capabilities app with a working store is a
34
+ // different observation, and if it is worth reporting it needs its own kind and its own words.
35
+ if (facts.stateUnwatched && (false === facts.hasCapabilities || facts.stateAsked)) {
36
+ // Same missing thing, two ways of meeting it — and the sentence has to say which. Describing an
37
+ // assertion about state to an agent that made no such assertion is a false explanation, and a
38
+ // gap nobody can act on is worse than no gap: it costs the trip and teaches the wrong lesson.
39
+ const declaredNothing = false === facts.hasCapabilities;
40
+ gaps.push(instrumentationGap(InstrumentationGapKind.NO_STORE_REGISTERED, declaredNothing
41
+ ? 'this app declared no capabilities at all, so no state can be read from it'
42
+ : 'no store is registered, and this assertion was about state', declaredNothing
43
+ ? 'every verdict here rests on what the DOM happens to show, and reticle_state will stay empty however many flows are driven'
44
+ : 'the assertion could not be answered from the deterministic channel and had to fall back to what the DOM happens to show'));
12
45
  }
13
46
  // The app moved and said nothing, and Reticle could not prove the outcome another way.
14
47
  if (facts.domMutated && 0 === facts.signalsFired && true !== facts.proved) {
15
- gaps.push(instrumentationGap(InstrumentationGapKind.NO_SIGNAL_ON_MUTATION, 'the DOM changed and no signal fired for it', 'the outcome had to be inferred from the DOM instead of read from the app asserting its own success, which is the strongest evidence available', { ...(facts.ref === undefined ? {} : { ref: facts.ref }) }));
48
+ gaps.push(instrumentationGap(InstrumentationGapKind.NO_SIGNAL_ON_MUTATION, 'the DOM changed and no signal fired for it', 'the outcome had to be inferred from the DOM instead of read from the app asserting its own success, which is the strongest evidence available',
49
+ // The one gap here that can be LOCATED. It is about the element that was driven, and that
50
+ // element is exactly what `source` describes — so the pointer is a fact, not a guess. It
51
+ // also outlives the `ref` beside it, which is what makes this gap still actionable when
52
+ // coverage reads it back long after the page has re-rendered.
53
+ {
54
+ ...(facts.ref === undefined ? {} : { ref: facts.ref }),
55
+ ...(facts.source === undefined ? {} : { source: facts.source }),
56
+ }));
16
57
  }
58
+ // Also unlocated, for the same reason: the remedy is a router adapter wired app-wide, not a line
59
+ // at the control that happened to trigger this navigation.
17
60
  if (facts.routeChanged && !facts.routeSignalFired) {
18
61
  gaps.push(instrumentationGap(InstrumentationGapKind.NO_ROUTE_SIGNAL, 'the route changed and nothing signalled it', 'route consequences cannot be asserted on this app, so a navigation can only be checked by what rendered afterwards'));
19
62
  }
63
+ // The one gap here that is not about the app. Deliberately UNLOCATED and deliberately generic: the
64
+ // only honest thing to report is that the intent is absent. Naming the file that changed, or the
65
+ // control that was driven, would invite the reader to treat a guess as the declaration — and a
66
+ // guessed intent reads as the developer's own words, which is strictly worse than honest absence.
67
+ if (true === facts.changeUndeclared) {
68
+ gaps.push(instrumentationGap(InstrumentationGapKind.UNDECLARED_CHANGE, 'code changed since the last verdict and no intent says what the change was for', 'this verdict can only check the app against itself, so a green here means "nothing visibly broke" rather than "the change did what it was meant to"'));
69
+ }
70
+ /**
71
+ * Say how old the debt is, when it is old enough to change what the reader should do.
72
+ *
73
+ * The ledger is a PROJECT's, deliberately — "what does this still owe" outlives one session. But an
74
+ * intent declared minutes ago and one abandoned last week produce the same sentence, and the second
75
+ * one is not about the work in front of you. Naming the age separates them without weakening the
76
+ * gap, and without pretending a stale backlog is this verdict's fault.
77
+ */
78
+ const DAY_MS = 86_400_000;
79
+ function describeIntentAge(ageMs) {
80
+ if (ageMs === undefined || ageMs < DAY_MS)
81
+ return '';
82
+ const days = Math.floor(ageMs / DAY_MS);
83
+ return ` (the oldest for ${String(days)} day${1 === days ? '' : 's'} — a backlog this old is probably not what this run is about; retire them or prove them)`;
84
+ }
85
+ // A green that does not settle what the run OWES.
86
+ //
87
+ // The mirror of `changeUndeclared` above, and the more expensive half: that fires when nothing was
88
+ // declared, this when something was and no verdict ever proved it. Measured on the bench fixture —
89
+ // an agent drew SEVEN green verdicts and reported FIXED on a form that still accepted a
90
+ // whitespace-only service, quoting its own patch as the evidence. Every verdict was honestly
91
+ // green; none of them was about the thing claimed, and nothing in the result said so.
92
+ //
93
+ // Only on a PASS: a failing verdict proved nothing and already says that, so adding this would be
94
+ // two sentences for one fact. It downgrades nothing either — the assertion did hold — it just
95
+ // names the debt on the result the agent is already reading, while it is deciding if it is done.
96
+ // `proved`, not `pass`, because a bare `{ settled }` wait scores the predicate true while proving
97
+ // nothing — the verdict comes back `no-fault`, and keying on `pass` attached "this verdict passed"
98
+ // to a result whose own `because` says it is not verification.
99
+ const owed = facts.openIntentCount ?? 0;
100
+ if (facts.pass && true === facts.proved && 0 < owed) {
101
+ gaps.push(instrumentationGap(InstrumentationGapKind.INTENT_UNDISCHARGED, `this verdict passed, and ${String(owed)} declared intent(s) are still unproved${describeIntentAge(facts.oldestOpenIntentAgeMs)}`, 'a green settles what it asserted, not what the run set out to do — reporting done here rests on the change looking right rather than on anything having checked it'));
102
+ }
20
103
  return dedupeGaps(gaps);
21
104
  }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Which tools this session called, and how reachable each one was.
3
+ *
4
+ * The generalisation of the capture ledger: it recorded a short list of read tools to answer one
5
+ * feature's disproof, and the question that followed is the same question asked of the whole table —
6
+ * most of the surface has never been observed being called at all, and a decision about what to cut
7
+ * or consolidate is about to be made. This is the instrument for that decision, and nothing else.
8
+ *
9
+ * ## Why the count alone is not enough
10
+ *
11
+ * A tool that goes uncalled because nobody was told it exists and a tool that goes uncalled because
12
+ * nobody wants it look identical in a call count. They are opposite conclusions. So every count is
13
+ * reported beside the tool's REACHABILITY — advertised on the default surface, advertised only on
14
+ * the extended one, or reachable solely by naming it to `reticle_run` — and the interesting cell is
15
+ * the one where an agent found a run-only tool anyway.
16
+ *
17
+ * ## The tiers are derived, never restated
18
+ *
19
+ * `tierOfTool` reads `CORE_TOOL_NAMES` and `EXTENDED_TOOL_NAMES` directly. A tool promoted or
20
+ * demoted between surfaces changes tier here with nothing edited. A local copy of either list would
21
+ * go stale on the first move and would then be wrong about the exact axis this exists to measure —
22
+ * and it would be wrong silently, in a report used to justify deleting things.
23
+ *
24
+ * ## What these numbers do NOT establish
25
+ *
26
+ * - **One session is not a hit rate.** This is evidence about ONE run by ONE agent against ONE app.
27
+ * A hit rate is a distribution over many sessions; a single session quoted as one is an anecdote
28
+ * with a denominator drawn on it.
29
+ * - **Never-called is not useless.** A tool one team reaches for once a month in an emergency and a
30
+ * tool nobody has ever called are the same zero here. Frequency alone cannot separate them, so a
31
+ * cut decided on frequency alone will delete the wrong things.
32
+ * - **Absent is not zero.** A session with no ledger reports `observed: false`. It has not
33
+ * established that no tools were called; it has established that nothing was watching.
34
+ * - The tools that ran BEFORE this instrument existed in the process, or against a session this
35
+ * daemon adopted after the fact, are simply missing rather than counted low.
36
+ */
37
+ /** How an agent could have reached a tool. Derived from the surface sets — see above. */
38
+ export declare const ToolTier: {
39
+ /** Advertised directly on the surface every user gets. */
40
+ readonly DEFAULT: "default";
41
+ /** Advertised only when the full schema-carrying surface is switched on. */
42
+ readonly EXTENDED: "extended";
43
+ /** Advertised nowhere: reachable only by naming it to `reticle_run`. */
44
+ readonly RUN_ONLY: "run-only";
45
+ };
46
+ export type ToolTier = (typeof ToolTier)[keyof typeof ToolTier];
47
+ /** Which surface would have put this tool in front of an agent, read off that surface's own set. */
48
+ export declare function tierOfTool(tool: string): ToolTier;
49
+ /** How many tools fell in each tier on one side of the called/never-called split. */
50
+ export interface TierCounts {
51
+ default: number;
52
+ extended: number;
53
+ runOnly: number;
54
+ }
55
+ export interface ToolHitRate {
56
+ /** False when this session carries no ledger — "not watched", never "nothing was called". */
57
+ observed: boolean;
58
+ /** The four-plus-two cells: every tier, called and never-called. Omitted when not observed. */
59
+ cells?: {
60
+ called: TierCounts;
61
+ neverCalled: TierCounts;
62
+ };
63
+ /** Each tool called this session, most-called first. */
64
+ called?: readonly {
65
+ tool: string;
66
+ tier: ToolTier;
67
+ calls: number;
68
+ }[];
69
+ /** Each tool in the table this session never called, by name. */
70
+ neverCalled?: readonly {
71
+ tool: string;
72
+ tier: ToolTier;
73
+ }[];
74
+ }
75
+ /**
76
+ * Cross one session's per-tool counts with the tool table.
77
+ *
78
+ * `allTools` is the DISPATCHABLE table — the names `reticle_run` will accept — passed in rather than
79
+ * imported, because the table is assembled in the module that owns this tool and importing it back
80
+ * would close a cycle. A tool counted but absent from the table still appears, so a name the table
81
+ * lost is visible rather than dropped.
82
+ */
83
+ export declare function foldToolHitRate(input: {
84
+ calls: ReadonlyMap<string, number> | undefined;
85
+ allTools: readonly string[];
86
+ }): ToolHitRate;