@reticlehq/server 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. package/SKILL.md +30 -28
  2. package/dist/command/cli/adhoc-suite.d.ts +19 -0
  3. package/dist/command/cli/adhoc-suite.js +88 -0
  4. package/dist/command/cli/adhoc-verdict.d.ts +23 -2
  5. package/dist/command/cli/adhoc-verdict.js +18 -10
  6. package/dist/command/cli/auth/device-flow.d.ts +2 -2
  7. package/dist/command/cli/cli-doctor.js +37 -5
  8. package/dist/command/cli/cli-flow-commands.d.ts +2 -1
  9. package/dist/command/cli/cli-kill.js +1 -1
  10. package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
  11. package/dist/command/cli/cli-parse-grammar.js +7 -0
  12. package/dist/command/cli/cli-parse-verify.d.ts +2 -5
  13. package/dist/command/cli/cli-parse-verify.js +86 -13
  14. package/dist/command/cli/cli-parse.d.ts +8 -1
  15. package/dist/command/cli/cli-parse.js +38 -7
  16. package/dist/command/cli/cli-verify.d.ts +59 -0
  17. package/dist/command/cli/cli-verify.js +131 -19
  18. package/dist/command/cli/cloud-cli.js +7 -6
  19. package/dist/command/cli/cloud-kit.d.ts +0 -2
  20. package/dist/command/cli/cloud-kit.js +3 -7
  21. package/dist/command/cli/config/config-discovery.d.ts +12 -0
  22. package/dist/command/cli/config/config-discovery.js +19 -0
  23. package/dist/command/cli/doctor/doctor-rows.d.ts +12 -0
  24. package/dist/command/cli/doctor/doctor-rows.js +12 -0
  25. package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
  26. package/dist/command/cli/drive/drive-command.js +1 -1
  27. package/dist/command/cli/launch/cli-launch.d.ts +3 -23
  28. package/dist/command/cli/launch/cli-launch.js +29 -70
  29. package/dist/command/cli/lifecycle/daemon-lifecycle.d.ts +33 -0
  30. package/dist/command/cli/lifecycle/daemon-lifecycle.js +198 -0
  31. package/dist/command/cli/mcp-command.js +5 -3
  32. package/dist/command/cli/ports/resolve/cli-port.d.ts +15 -1
  33. package/dist/command/cli/ports/resolve/cli-port.js +50 -2
  34. package/dist/command/cli/report-command.d.ts +16 -0
  35. package/dist/command/cli/report-command.js +97 -0
  36. package/dist/command/cli/status/status-lines.d.ts +28 -0
  37. package/dist/command/cli/status/status-lines.js +117 -0
  38. package/dist/command/cli.d.ts +1 -1
  39. package/dist/command/cli.js +29 -177
  40. package/dist/command/daemon/binding/daemon-status-probe.d.ts +32 -0
  41. package/dist/command/daemon/binding/daemon-status-probe.js +93 -0
  42. package/dist/command/daemon/binding/loopback-reach.d.ts +42 -0
  43. package/dist/command/daemon/binding/loopback-reach.js +63 -0
  44. package/dist/command/daemon/binding/port-presence.d.ts +1 -1
  45. package/dist/command/daemon/daemon-resilience.d.ts +13 -1
  46. package/dist/command/daemon/daemon-resilience.js +12 -1
  47. package/dist/command/daemon/daemon.d.ts +2 -11
  48. package/dist/command/daemon/daemon.js +8 -11
  49. package/dist/command/daemon/idle-shutdown.d.ts +2 -1
  50. package/dist/command/daemon/lifetime/heartbeat.d.ts +8 -0
  51. package/dist/command/daemon/lifetime/heartbeat.js +10 -0
  52. package/dist/command/daemon/lifetime/log-cap.d.ts +68 -0
  53. package/dist/command/daemon/lifetime/log-cap.js +115 -0
  54. package/dist/command/setup/agent-notice-memory.d.ts +16 -0
  55. package/dist/command/setup/agent-notice-memory.js +65 -0
  56. package/dist/command/setup/bringup/dev-server-wait.d.ts +1 -0
  57. package/dist/command/setup/bringup/dev-server-wait.js +16 -0
  58. package/dist/command/setup/bringup/ensure-daemon.js +1 -1
  59. package/dist/command/setup/init/init-host.js +8 -0
  60. package/dist/command/setup/init/init-runtime.js +13 -1
  61. package/dist/command/setup/node-effects.js +1 -1
  62. package/dist/command/setup/probe/page-probe.js +9 -2
  63. package/dist/command/setup/run-setup.d.ts +25 -7
  64. package/dist/command/setup/run-setup.js +51 -9
  65. package/dist/command/setup/setup-command.js +31 -3
  66. package/dist/command/setup/terminal/confirm.js +2 -1
  67. package/dist/command/update/updater.d.ts +27 -0
  68. package/dist/command/update/updater.js +35 -2
  69. package/dist/features/crawl/crawl-tools.js +3 -1
  70. package/dist/features/crawl/crawl.d.ts +2 -0
  71. package/dist/features/crawl/crawl.js +3 -1
  72. package/dist/features/harness/drive-report.d.ts +73 -0
  73. package/dist/features/harness/drive-report.js +196 -0
  74. package/dist/features/harness/driver.js +29 -8
  75. package/dist/features/harness/drivers.d.ts +19 -0
  76. package/dist/features/harness/drivers.js +19 -0
  77. package/dist/features/harness/fill-values.d.ts +67 -0
  78. package/dist/features/harness/fill-values.js +153 -0
  79. package/dist/features/harness/jev-driver.d.ts +188 -0
  80. package/dist/features/harness/jev-driver.js +661 -0
  81. package/dist/features/harness/openai-driver.d.ts +46 -0
  82. package/dist/features/harness/openai-driver.js +197 -0
  83. package/dist/features/harness/platform-config.d.ts +62 -0
  84. package/dist/features/harness/platform-config.js +97 -0
  85. package/dist/features/visual/visual-tools.js +26 -2
  86. package/dist/index.d.ts +1 -1
  87. package/dist/index.js +80 -130
  88. package/dist/judgement/capsule/capsule-store.d.ts +31 -2
  89. package/dist/judgement/capsule/capsule-store.js +58 -6
  90. package/dist/judgement/capsule/capsule.js +5 -0
  91. package/dist/judgement/capsule/causal-summary.d.ts +13 -1
  92. package/dist/judgement/capsule/causal-summary.js +7 -0
  93. package/dist/judgement/domain/domain-model.js +38 -9
  94. package/dist/judgement/domain/domain-tools.js +4 -2
  95. package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
  96. package/dist/judgement/phenomena/phenomena.d.ts +2 -1
  97. package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
  98. package/dist/judgement/runs/artifact/gap-report.js +46 -0
  99. package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
  100. package/dist/judgement/runs/artifact/gap-summary.js +85 -0
  101. package/dist/judgement/runs/artifact/render-report.js +4 -1
  102. package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
  103. package/dist/judgement/runs/artifact/to-artifact.js +1 -1
  104. package/dist/judgement/runs/context-tools.js +21 -3
  105. package/dist/judgement/runs/drive-run.js +7 -2
  106. package/dist/judgement/runs/verification-sync.d.ts +2 -2
  107. package/dist/judgement/runs/verification-sync.js +9 -2
  108. package/dist/language/flows/affected-tools.js +6 -3
  109. package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
  110. package/dist/language/flows/annotate-notes/annotate.js +66 -18
  111. package/dist/language/flows/flow-anchor.d.ts +12 -0
  112. package/dist/language/flows/flow-anchor.js +20 -0
  113. package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
  114. package/dist/language/flows/flow-expect-grammar.js +92 -11
  115. package/dist/language/flows/flow-intent.js +1 -1
  116. package/dist/language/flows/flow-learning.d.ts +3 -0
  117. package/dist/language/flows/flow-learning.js +39 -8
  118. package/dist/language/flows/flow-replay-run.d.ts +55 -10
  119. package/dist/language/flows/flow-replay-run.js +217 -58
  120. package/dist/language/flows/flow-replay-types.d.ts +9 -0
  121. package/dist/language/flows/flow-replay.d.ts +29 -11
  122. package/dist/language/flows/flow-replay.js +221 -203
  123. package/dist/language/flows/flow-result.d.ts +18 -1
  124. package/dist/language/flows/flow-result.js +18 -2
  125. package/dist/language/flows/flow-step-runners.d.ts +26 -10
  126. package/dist/language/flows/flow-step-runners.js +20 -4
  127. package/dist/language/flows/flow-store-for-session.d.ts +2 -1
  128. package/dist/language/flows/flow-success.d.ts +21 -6
  129. package/dist/language/flows/flow-success.js +50 -138
  130. package/dist/language/flows/flow-tools.js +8 -4
  131. package/dist/language/flows/flows.d.ts +15 -10
  132. package/dist/language/flows/flows.js +17 -4
  133. package/dist/language/flows/heal.d.ts +9 -1
  134. package/dist/language/flows/heal.js +24 -2
  135. package/dist/language/flows/learned-sources.d.ts +21 -0
  136. package/dist/language/flows/learned-sources.js +84 -0
  137. package/dist/language/flows/narrow-write.d.ts +4 -4
  138. package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
  139. package/dist/language/flows/recording/recording-backtrack.js +1 -1
  140. package/dist/language/flows/recording/replay-halt.d.ts +2 -1
  141. package/dist/language/flows/recording/tape/recordings.d.ts +2 -2
  142. package/dist/language/flows/replay.js +13 -9
  143. package/dist/language/flows/stores/annotation-store.d.ts +5 -5
  144. package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
  145. package/dist/language/flows/stores/flake-store.js +2 -3
  146. package/dist/language/flows/suite/coverage.d.ts +1 -1
  147. package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
  148. package/dist/language/flows/suite-selection.d.ts +15 -1
  149. package/dist/language/flows/suite-selection.js +1 -1
  150. package/dist/language/flows/verify-change-tools.js +21 -4
  151. package/dist/memory/cloud/account-state.d.ts +1 -1
  152. package/dist/memory/cloud/account-state.js +5 -4
  153. package/dist/memory/cloud/cloud-config.d.ts +9 -0
  154. package/dist/memory/cloud/cloud-config.js +9 -0
  155. package/dist/memory/cloud/cloud-sync.d.ts +7 -4
  156. package/dist/memory/cloud/cloud-sync.js +11 -6
  157. package/dist/memory/cloud/harness-config.d.ts +12 -0
  158. package/dist/memory/cloud/harness-config.js +58 -0
  159. package/dist/memory/cloud/harness-offer.d.ts +44 -0
  160. package/dist/memory/cloud/harness-offer.js +146 -0
  161. package/dist/memory/cloud/harness-switch.d.ts +11 -0
  162. package/dist/memory/cloud/harness-switch.js +53 -0
  163. package/dist/memory/cloud/sync-cycle.d.ts +12 -0
  164. package/dist/memory/cloud/sync-cycle.js +29 -4
  165. package/dist/memory/cloud/sync-daemon.d.ts +35 -2
  166. package/dist/memory/cloud/sync-daemon.js +45 -0
  167. package/dist/memory/cloud/sync-disk.js +11 -6
  168. package/dist/memory/impact/impact-recorder.d.ts +8 -0
  169. package/dist/memory/impact/impact-recorder.js +2 -1
  170. package/dist/memory/impact/impact-store.d.ts +5 -0
  171. package/dist/memory/impact/impact-store.js +25 -0
  172. package/dist/memory/intent/inline-intent.js +8 -3
  173. package/dist/memory/intent/intent-shard-store.d.ts +33 -47
  174. package/dist/memory/intent/intent-shard-store.js +253 -141
  175. package/dist/memory/intent/intent-shard.d.ts +22 -23
  176. package/dist/memory/intent/intent-shard.js +1 -1
  177. package/dist/memory/intent/intent-store.d.ts +13 -8
  178. package/dist/memory/intent/intent-store.js +46 -90
  179. package/dist/memory/intent/intent-tools.js +34 -23
  180. package/dist/memory/journal/ambient-store.js +2 -3
  181. package/dist/memory/journal/attach-journal.d.ts +11 -0
  182. package/dist/memory/journal/attach-journal.js +1 -1
  183. package/dist/memory/journal/deviation-report.js +4 -1
  184. package/dist/memory/journal/deviation-service.js +10 -2
  185. package/dist/memory/journal/drive-flow.d.ts +2 -2
  186. package/dist/memory/journal/drive-flow.js +21 -12
  187. package/dist/memory/journal/envelope-store.js +2 -3
  188. package/dist/memory/journal/journal-query.d.ts +35 -1
  189. package/dist/memory/journal/journal-query.js +46 -0
  190. package/dist/memory/journal/journal-recorder.d.ts +71 -2
  191. package/dist/memory/journal/journal-recorder.js +45 -7
  192. package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
  193. package/dist/memory/journal/on-disk/retain-policy.js +44 -0
  194. package/dist/memory/journal/on-disk/retention.d.ts +91 -2
  195. package/dist/memory/journal/on-disk/retention.js +181 -11
  196. package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
  197. package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
  198. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -2
  199. package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -38
  200. package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
  201. package/dist/memory/journal/on-disk/workspace-tiers.js +88 -0
  202. package/dist/memory/journal/route-template.d.ts +22 -0
  203. package/dist/memory/journal/route-template.js +44 -0
  204. package/dist/memory/journal/self-observation.d.ts +3 -0
  205. package/dist/memory/journal/self-observation.js +67 -0
  206. package/dist/memory/journal/session-end.d.ts +17 -4
  207. package/dist/memory/journal/session-end.js +85 -7
  208. package/dist/memory/journal/session-journal.d.ts +122 -5
  209. package/dist/memory/journal/session-journal.js +489 -32
  210. package/dist/memory/project/artifact-root-resolver.d.ts +3 -0
  211. package/dist/memory/project/artifact-root-resolver.js +89 -0
  212. package/dist/memory/project/artifact-root.d.ts +39 -2
  213. package/dist/memory/project/artifact-root.js +58 -1
  214. package/dist/memory/project/baselines.d.ts +1 -1
  215. package/dist/memory/project/dir/fill-value-store.d.ts +32 -0
  216. package/dist/memory/project/dir/fill-value-store.js +88 -0
  217. package/dist/memory/project/dir/reticle-dir.d.ts +15 -5
  218. package/dist/memory/project/dir/reticle-dir.js +11 -0
  219. package/dist/memory/project/fs/fs-port.d.ts +22 -1
  220. package/dist/memory/project/fs/fs-port.js +72 -5
  221. package/dist/memory/project/fs/write-atomic.d.ts +26 -0
  222. package/dist/memory/project/fs/write-atomic.js +48 -0
  223. package/dist/memory/project/learned-routes.d.ts +16 -2
  224. package/dist/memory/project/learned-routes.js +26 -10
  225. package/dist/memory/project/project-for-root.d.ts +30 -0
  226. package/dist/memory/project/project-for-root.js +43 -0
  227. package/dist/memory/project/project-tools.js +10 -5
  228. package/dist/memory/project/session-root.d.ts +35 -6
  229. package/dist/memory/project/session-root.js +70 -8
  230. package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
  231. package/dist/memory/recall/prior/connection-memory.js +8 -0
  232. package/dist/memory/recall/registered-projects.d.ts +2 -1
  233. package/dist/portal/bridge/auth-failure-reason.d.ts +9 -0
  234. package/dist/portal/bridge/auth-failure-reason.js +20 -3
  235. package/dist/portal/bridge/bridge.d.ts +6 -1
  236. package/dist/portal/bridge/bridge.js +36 -2
  237. package/dist/portal/input/real-input.js +2 -1
  238. package/dist/portal/pool/browser-pool.d.ts +10 -0
  239. package/dist/portal/pool/browser-pool.js +10 -0
  240. package/dist/portal/pool/playwright-launcher.js +1 -0
  241. package/dist/portal/pool/pool-contract.d.ts +2 -0
  242. package/dist/portal/pool/storage-seed.d.ts +2 -1
  243. package/dist/portal/pool/zero-install.d.ts +27 -0
  244. package/dist/portal/pool/zero-install.js +54 -0
  245. package/dist/portal/realm/web-realm.js +6 -0
  246. package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
  247. package/dist/portal/session/dev-server/served-document.d.ts +21 -0
  248. package/dist/portal/session/dev-server/served-document.js +57 -0
  249. package/dist/portal/session/facts/background-traffic.d.ts +9 -0
  250. package/dist/portal/session/facts/background-traffic.js +25 -0
  251. package/dist/portal/session/facts/session-replaced.d.ts +19 -0
  252. package/dist/portal/session/facts/session-replaced.js +22 -0
  253. package/dist/portal/session/live-control-tools.js +48 -3
  254. package/dist/portal/session/no-session-diagnosis.d.ts +1 -0
  255. package/dist/portal/session/no-session-diagnosis.js +67 -60
  256. package/dist/portal/session/no-session-next-action.js +3 -2
  257. package/dist/portal/session/no-session-watch.js +14 -10
  258. package/dist/portal/session/presence/attachment-history.d.ts +13 -2
  259. package/dist/portal/session/rebind.d.ts +18 -0
  260. package/dist/portal/session/rebind.js +38 -0
  261. package/dist/portal/session/session-health.d.ts +61 -0
  262. package/dist/portal/session/session-health.js +40 -1
  263. package/dist/portal/session/session-info.d.ts +6 -0
  264. package/dist/portal/session/session-manager.d.ts +13 -0
  265. package/dist/portal/session/session-manager.js +42 -3
  266. package/dist/portal/session/session-successor.d.ts +11 -0
  267. package/dist/portal/session/session-successor.js +14 -1
  268. package/dist/portal/session/session-verdict-facts.d.ts +28 -0
  269. package/dist/portal/session/session-verdict-facts.js +15 -0
  270. package/dist/portal/session/session.d.ts +26 -10
  271. package/dist/portal/session/session.js +48 -49
  272. package/dist/status-payload.d.ts +12 -1
  273. package/dist/status-payload.js +4 -1
  274. package/dist/surface/http-server.js +3 -5
  275. package/dist/surface/mcp/faults/mcp-outage.d.ts +2 -1
  276. package/dist/surface/mcp/faults/mcp-outage.js +20 -4
  277. package/dist/surface/mcp/mcp-proxy.d.ts +10 -1
  278. package/dist/surface/mcp/mcp-proxy.js +23 -12
  279. package/dist/surface/mcp/mcp.d.ts +10 -1
  280. package/dist/surface/mcp/mcp.js +59 -29
  281. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +15 -2
  282. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +22 -5
  283. package/dist/surface/mcp/proxy/proxy-log.d.ts +5 -11
  284. package/dist/surface/mcp/proxy/proxy-log.js +7 -22
  285. package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
  286. package/dist/surface/mcp/server-instructions.js +12 -1
  287. package/dist/surface/tools/act/act-preflight.js +6 -1
  288. package/dist/surface/tools/act/already-true.d.ts +40 -0
  289. package/dist/surface/tools/act/already-true.js +36 -0
  290. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +6 -0
  291. package/dist/surface/tools/act/navigation/navigate-arrival.js +13 -0
  292. package/dist/surface/tools/act/sequence-grade.js +13 -0
  293. package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
  294. package/dist/surface/tools/act/settle-in-flight.js +29 -6
  295. package/dist/surface/tools/act-capsule.js +49 -8
  296. package/dist/surface/tools/act-tools.js +25 -24
  297. package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
  298. package/dist/surface/tools/args/numeric-bounds.js +30 -3
  299. package/dist/surface/tools/assert/assert-grade.js +14 -2
  300. package/dist/surface/tools/assert/assert-verdict.js +74 -21
  301. package/dist/surface/tools/browser-tools.js +4 -3
  302. package/dist/surface/tools/coverage-tools.js +5 -2
  303. package/dist/surface/tools/dynamic-tools.js +43 -46
  304. package/dist/surface/tools/error-recovery.d.ts +6 -0
  305. package/dist/surface/tools/error-recovery.js +15 -0
  306. package/dist/surface/tools/explore-tools.js +80 -7
  307. package/dist/surface/tools/feedback-tools.d.ts +2 -1
  308. package/dist/surface/tools/feedback-tools.js +50 -3
  309. package/dist/surface/tools/first-sentence.d.ts +24 -0
  310. package/dist/surface/tools/first-sentence.js +0 -0
  311. package/dist/surface/tools/harness-explore.d.ts +125 -1
  312. package/dist/surface/tools/harness-explore.js +401 -15
  313. package/dist/surface/tools/harness-plan.d.ts +76 -0
  314. package/dist/surface/tools/harness-plan.js +156 -0
  315. package/dist/surface/tools/harness-toolset.js +12 -1
  316. package/dist/surface/tools/invoke-tool.js +82 -9
  317. package/dist/surface/tools/lease-hint.d.ts +8 -0
  318. package/dist/surface/tools/lease-hint.js +10 -0
  319. package/dist/surface/tools/lease-tools.d.ts +16 -28
  320. package/dist/surface/tools/lease-tools.js +53 -5
  321. package/dist/surface/tools/lineage-tools.js +1 -1
  322. package/dist/surface/tools/live-call-text.d.ts +6 -0
  323. package/dist/surface/tools/live-call-text.js +36 -2
  324. package/dist/surface/tools/observe-tools.js +69 -15
  325. package/dist/surface/tools/read/query-windowed.d.ts +1 -1
  326. package/dist/surface/tools/read/query-windowed.js +1 -1
  327. package/dist/surface/tools/read-tools.js +1 -2
  328. package/dist/surface/tools/real-input-attempt.d.ts +0 -5
  329. package/dist/surface/tools/real-input-attempt.js +1 -1
  330. package/dist/surface/tools/tool-kit.d.ts +23 -3
  331. package/dist/surface/tools/tool-kit.js +6 -1
  332. package/dist/surface/tools/tool-surface.js +17 -5
  333. package/dist/surface/tools/tools-helpers.d.ts +8 -7
  334. package/dist/surface/tools/tools-helpers.js +11 -13
  335. package/dist/surface/tools/tools.js +70 -30
  336. package/dist/telemetry/bug-found.js +32 -2
  337. package/dist/telemetry/feedback-context.js +3 -3
  338. package/dist/telemetry/feedback-local.d.ts +1 -0
  339. package/dist/telemetry/feedback-local.js +1 -3
  340. package/dist/telemetry/feedback.d.ts +1 -1
  341. package/dist/telemetry/harness-drive.d.ts +6 -0
  342. package/dist/telemetry/harness-drive.js +39 -0
  343. package/dist/telemetry/hud-metrics.d.ts +32 -0
  344. package/dist/telemetry/hud-metrics.js +95 -0
  345. package/dist/telemetry/mcp-connection.d.ts +14 -1
  346. package/dist/telemetry/mcp-connection.js +17 -2
  347. package/dist/telemetry/onboarding-firsts.d.ts +30 -1
  348. package/dist/telemetry/onboarding-firsts.js +54 -12
  349. package/dist/telemetry/session-metrics.d.ts +5 -0
  350. package/dist/telemetry/session-metrics.js +15 -1
  351. package/dist/telemetry/verification-of.js +31 -3
  352. package/dist/wire-journal.d.ts +31 -0
  353. package/dist/wire-journal.js +100 -0
  354. package/docs/README.md +1 -0
  355. package/docs/agent-cheatsheet.md +7 -3
  356. package/docs/autodrive.md +42 -2
  357. package/docs/benchmarks.md +1 -1
  358. package/docs/capabilities.mdx +1 -1
  359. package/docs/cli/cloud.mdx +11 -9
  360. package/docs/cli/doctor.mdx +1 -0
  361. package/docs/cli/init.mdx +1 -0
  362. package/docs/cli/report.mdx +44 -0
  363. package/docs/cli/status.mdx +35 -20
  364. package/docs/cli.mdx +8 -18
  365. package/docs/desktop.mdx +1 -1
  366. package/docs/faq.mdx +1 -1
  367. package/docs/flows.md +11 -0
  368. package/docs/frameworks.mdx +113 -1
  369. package/docs/install-agentic.mdx +36 -0
  370. package/docs/install-manual.mdx +1 -1
  371. package/docs/packages/tauri.mdx +1 -1
  372. package/docs/packages.mdx +1 -1
  373. package/docs/predicates.mdx +144 -4
  374. package/docs/system-map.md +6 -4
  375. package/docs/telemetry.md +2 -1
  376. package/docs/token-efficiency.md +2 -2
  377. package/docs/tools/overview.mdx +1 -1
  378. package/docs/tools/query.mdx +10 -10
  379. package/docs/troubleshooting.mdx +8 -2
  380. package/docs/usage.md +2 -2
  381. package/docs/what-is-recorded.md +62 -0
  382. package/package.json +5 -5
  383. package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
  384. package/dist/judgement/outcome/predicate-to-expect.js +0 -148
@@ -0,0 +1,196 @@
1
+ /**
2
+ * What a drive did, written from the evidence rather than narrated by the model that drove.
3
+ *
4
+ * `HarnessResult.summary` is the driver's own account, and it has always carried a warning that
5
+ * nothing downstream grades it. That was a caveat when the driver was a language model. It became a
6
+ * hole the moment a System One model could drive: Jev answers typed questions and cannot produce a
7
+ * sentence, so `summary` came back EMPTY, and an agent calling `explore` got a result with no
8
+ * account of what had happened in it at all.
9
+ *
10
+ * Restoring prose would have been the obvious fix and the wrong one. The drive already records
11
+ * every call it made, with the arguments it used and the verdict the engine returned — so the
12
+ * account can be DERIVED. That is strictly better than a narration: a model summarising its own
13
+ * drive is the one witness with a reason to round "unknown" up to "worked", and this cannot, because
14
+ * it is not writing about the drive, it is reading it.
15
+ *
16
+ * Written for the agent on the other end of the MCP call. It leads with what was proved, because
17
+ * that is what a coding agent has to gate on, and it says "not proved" in those words rather than
18
+ * leaving a verdict of `unknown` to be read as a pass.
19
+ */
20
+ import { asRecord, asString, ReticleTool, Verified } from '@reticlehq/core';
21
+ /** How many steps are listed before the account starts counting instead of naming. */
22
+ const MAX_LISTED = 12;
23
+ /** Actions that change the app. Reads are not part of the story of what was driven. */
24
+ const DRIVING_TOOLS = new Set([
25
+ ReticleTool.ACT,
26
+ ReticleTool.ACT_AND_WAIT,
27
+ ReticleTool.ACT_SEQUENCE,
28
+ ReticleTool.NAVIGATE,
29
+ ]);
30
+ /**
31
+ * The human-readable name of what was acted on.
32
+ *
33
+ * The ref (`e42`) is meaningless to the agent reading this — it is a handle that expired when the
34
+ * page changed. A description that says `button "Create"` survives being read tomorrow, so the ref
35
+ * is only the fallback.
36
+ */
37
+ function targetOf(args, result) {
38
+ const described = asString(result['element']) ?? asString(result['name']);
39
+ if (described !== undefined && 0 < described.length)
40
+ return described;
41
+ const target = asRecord(args['target']);
42
+ for (const key of ['testid', 'text', 'label', 'name']) {
43
+ const value = asString(target[key]);
44
+ if (value !== undefined && 0 < value.length)
45
+ return `${key}=${value}`;
46
+ }
47
+ const url = asString(args['url']);
48
+ if (url !== undefined && 0 < url.length)
49
+ return url;
50
+ return asString(args['ref']) ?? 'the page';
51
+ }
52
+ /**
53
+ * Replays this drive ran, and what each came back with.
54
+ *
55
+ * The verdict is `status`, not `passed`. Reading the wrong key reported ten replays as "undecided"
56
+ * on a run where every one of them had answered — which is the same false-nothing this report was
57
+ * written to stop, made by the report itself.
58
+ *
59
+ * Three outcomes, kept apart because they mean different things to whoever reads this. `ok` is the
60
+ * journey still holding. `error` is it failing. `drift` is the flow's own anchors no longer
61
+ * resolving — the app moved under a recording, which is a finding about the RECORDING rather than
62
+ * proof the feature broke, and collapsing it into "failed" would send somebody to fix working code.
63
+ */
64
+ export const ReplayOutcome = { OK: 'ok', DRIFT: 'drift', ERROR: 'error' };
65
+ export function replayedFlows(toolCalls) {
66
+ const out = [];
67
+ for (const call of toolCalls) {
68
+ if (ReticleTool.FLOW_REPLAY !== call.name)
69
+ continue;
70
+ const name = asString(asRecord(call.args)['flowName']) ?? 'a flow';
71
+ out.push({
72
+ name,
73
+ status: call.isError ? ReplayOutcome.ERROR : asString(asRecord(call.result)['status']),
74
+ });
75
+ }
76
+ return out;
77
+ }
78
+ /**
79
+ * The declared consequence, as a short phrase rather than a JSON dump.
80
+ *
81
+ * One line per kind, because a reader triaging a red needs "it claimed a POST" and not the
82
+ * predicate's wire shape. Unknown kinds fall back to the kind name, which is still more than the
83
+ * nothing this used to say.
84
+ */
85
+ function describeClaim(until) {
86
+ const p = asRecord(until);
87
+ const kind = asString(p['kind']);
88
+ if (kind === undefined)
89
+ return undefined;
90
+ if ('signal' === kind) {
91
+ const name = asString(p['name']);
92
+ return name === undefined ? 'a signal' : `signal ${name}`;
93
+ }
94
+ if ('net' === kind) {
95
+ const method = asString(p['method']);
96
+ const url = asString(p['urlContains']);
97
+ return `a ${method ?? ''} request${url === undefined ? '' : ` to ${url}`}`.replace(' ', ' ');
98
+ }
99
+ if ('route' === kind)
100
+ return `a route containing ${asString(p['contains']) ?? '?'}`;
101
+ if ('not' === kind) {
102
+ const inner = describeClaim(p['predicate']);
103
+ return inner === undefined ? 'not something' : `to leave ${inner}`;
104
+ }
105
+ if ('anyOf' === kind)
106
+ return 'a request or a signal';
107
+ return kind;
108
+ }
109
+ /** Reduce the raw call log to the actions that actually drove the app. */
110
+ export function drivenSteps(toolCalls) {
111
+ const steps = [];
112
+ for (const call of toolCalls) {
113
+ if (!DRIVING_TOOLS.has(call.name))
114
+ continue;
115
+ const args = asRecord(call.args);
116
+ const result = asRecord(call.result);
117
+ const action = ReticleTool.NAVIGATE === call.name ? 'navigate' : (asString(args['action']) ?? 'act');
118
+ steps.push({
119
+ action,
120
+ target: targetOf(args, result),
121
+ verified: call.isError ? 'error' : asString(result['verified']),
122
+ because: call.isError ? asString(result['error']) : asString(result['because']),
123
+ claimed: describeClaim(args['until']),
124
+ });
125
+ }
126
+ return steps;
127
+ }
128
+ /**
129
+ * The account, as lines.
130
+ *
131
+ * Proved and not-proved are counted separately and both are stated, because the failure mode this
132
+ * whole product exists to prevent is a reader taking "we did twelve things" for "twelve things
133
+ * work". `unknown` is deliberately grouped under NOT proved and labelled as undecided evidence
134
+ * rather than as a failure — it calls for a better check, not a code change, and collapsing the two
135
+ * would send an agent to rewrite working code.
136
+ */
137
+ export function describeDrive(toolCalls, savedFlows) {
138
+ const steps = drivenSteps(toolCalls);
139
+ const replays = replayedFlows(toolCalls);
140
+ /*
141
+ * A run that only REPLAYED is not a run that did nothing.
142
+ *
143
+ * "Nothing was driven" was true of the actions and wrong about the run: sixteen recorded journeys
144
+ * replayed deterministically, for zero model tokens, and the report called it empty. That is the
145
+ * cheap half of the plan working exactly as intended, and it has to read as a result.
146
+ */
147
+ const replayLine = 0 === replays.length
148
+ ? undefined
149
+ : (() => {
150
+ const held = replays.filter((r) => ReplayOutcome.OK === r.status);
151
+ const failed = replays.filter((r) => ReplayOutcome.ERROR === r.status);
152
+ const drifted = replays.filter((r) => ReplayOutcome.DRIFT === r.status);
153
+ const lines = [
154
+ `Replayed ${String(replays.length)} recorded journey(s) with NO model in the loop: ` +
155
+ `${String(held.length)} still hold, ${String(failed.length)} failed, ${String(drifted.length)} drifted.`,
156
+ ];
157
+ if (0 < failed.length)
158
+ lines.push(` FAILED: ${failed.map((r) => r.name).join(', ')} — regressions in journeys that used to pass.`);
159
+ if (0 < drifted.length)
160
+ lines.push(` DRIFTED: ${drifted.map((r) => r.name).join(', ')} — the app moved under these recordings; the flow needs re-anchoring, not the app fixing.`);
161
+ return lines.join('\n');
162
+ })();
163
+ if (0 === steps.length)
164
+ return (replayLine ??
165
+ 'Nothing was driven and nothing was replayed: the run made no action against the app.');
166
+ const proved = steps.filter((step) => Verified.YES === step.verified);
167
+ const failed = steps.filter((step) => Verified.NO === step.verified);
168
+ const undecided = steps.filter((step) => Verified.YES !== step.verified && Verified.NO !== step.verified);
169
+ const lines = [
170
+ ...(replayLine === undefined ? [] : [replayLine]),
171
+ `Drove ${String(steps.length)} action(s): ${String(proved.length)} proved, ` +
172
+ `${String(failed.length)} failed, ${String(undecided.length)} not decided.`,
173
+ ];
174
+ for (const step of steps.slice(0, MAX_LISTED)) {
175
+ const verdict = step.verified ?? 'nothing declared';
176
+ const because = step.because === undefined ? '' : ` — ${step.because}`;
177
+ // The claim, on failures only: it is what separates "the app is broken" from "the drive guessed
178
+ // wrong", and those two readings need opposite responses from whoever reads this.
179
+ // Shown on anything that did not pass, not just on a red. `no-fault` means "the consequence was
180
+ // already true, so this proved nothing" -- which is unreadable without knowing WHAT was claimed,
181
+ // and is the line that exposed a GET offer being satisfied by a dashboard's own polling.
182
+ const claimed = Verified.YES !== step.verified && step.claimed !== undefined
183
+ ? ` (claimed ${step.claimed})`
184
+ : '';
185
+ lines.push(` ${step.action} ${step.target}: ${verdict}${claimed}${because}`);
186
+ }
187
+ if (MAX_LISTED < steps.length)
188
+ lines.push(` … and ${String(steps.length - MAX_LISTED)} more.`);
189
+ if (0 < failed.length)
190
+ lines.push(`${String(failed.length)} action(s) FAILED — the app did not do what the drive declared it would. These are findings.`);
191
+ if (0 < undecided.length)
192
+ lines.push(`${String(undecided.length)} action(s) were NOT PROVED. That is undecided evidence, not a failure: it calls for a better check, not a code change.`);
193
+ if (0 < savedFlows.length)
194
+ lines.push(`Replay any of this without a model: reticle_verify { action: "flows" } — saved ${savedFlows.join(', ')}.`);
195
+ return lines.join('\n');
196
+ }
@@ -11,7 +11,7 @@
11
11
  * purpose.
12
12
  */
13
13
  import { z } from 'zod';
14
- import { ReticleEnv } from '@reticlehq/core';
14
+ import { ReticleEnv, apiKeyFrom } from '@reticlehq/core';
15
15
  /**
16
16
  * The default driver model.
17
17
  *
@@ -67,16 +67,37 @@ const MessageResponse = z.object({
67
67
  * routine answer a caller offers a way out of, not a failure.
68
68
  */
69
69
  export function harnessOptionsFromEnv(env) {
70
- const apiKey = env[ReticleEnv.HARNESS_KEY];
71
- if (apiKey === undefined || 0 === apiKey.length)
72
- return undefined;
73
70
  const model = env[ReticleEnv.HARNESS_MODEL];
74
71
  const baseUrl = env[ReticleEnv.HARNESS_BASE_URL];
75
- return {
76
- apiKey,
77
- ...(model === undefined || 0 === model.length ? {} : { model }),
78
- ...(baseUrl === undefined || 0 === baseUrl.length ? {} : { baseUrl }),
72
+ const withOptions = (apiKey, fallbackUrl) => {
73
+ const url = baseUrl === undefined || 0 === baseUrl.length ? fallbackUrl : baseUrl;
74
+ return {
75
+ apiKey,
76
+ ...(model === undefined || 0 === model.length ? {} : { model }),
77
+ ...(url === undefined || 0 === url.length ? {} : { baseUrl: url }),
78
+ };
79
79
  };
80
+ const apiKey = env[ReticleEnv.HARNESS_KEY];
81
+ if (apiKey !== undefined && 0 < apiKey.length)
82
+ return withOptions(apiKey);
83
+ /**
84
+ * The platform key, against the platform.
85
+ *
86
+ * Reticle's own service answers the Messages API at `/v1/messages` and forwards with ITS provider
87
+ * key, so a user who has run `reticle link` can drive with a frontier model having never held an
88
+ * Anthropic key at all. Without this they would be told to set `ANTHROPIC_API_KEY` to a value
89
+ * beginning `rk_live_` — which works, and reads like a mistake, and is the sort of instruction
90
+ * people correct on the way past and then cannot explain why nothing runs.
91
+ *
92
+ * An explicit `ANTHROPIC_API_KEY` still wins: someone who exported one meant it.
93
+ */
94
+ const cloudKey = apiKeyFrom(env);
95
+ const cloudUrl = env[ReticleEnv.CLOUD_URL];
96
+ if (cloudKey === undefined || 0 === cloudKey.length)
97
+ return undefined;
98
+ if (cloudUrl === undefined || 0 === cloudUrl.length)
99
+ return undefined;
100
+ return withOptions(cloudKey, cloudUrl);
80
101
  }
81
102
  /** Build a driver backed by the Messages API. */
82
103
  export function harnessDriver(options) {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The names of the drivers the harness can be asked for.
3
+ *
4
+ * A leaf module on purpose. These used to be re-exported from `harness-explore.ts`, which sits in
5
+ * the tool surface and is reached, through the toolset and the tool table, from the very tool that
6
+ * needs to declare them as an enum — a cycle that left the list `[undefined, undefined]` at the
7
+ * moment zod read it, and failed inside a JSON-schema parser rather than anywhere near the cause.
8
+ * Nothing here imports anything, so nothing can be half-initialised when it is read.
9
+ */
10
+ /** Generates its tool calls as text, and names the journeys it records. The default. */
11
+ export declare const ANTHROPIC_DRIVER_NAME = "anthropic";
12
+ /** Answers typed questions; picks from the elements on the page and cannot invent one. */
13
+ export declare const JEV_DRIVER_NAME = "jev";
14
+ /** Generates its tool calls, over Chat Completions. Here so an A/B has a third arm. */
15
+ export declare const OPENAI_DRIVER_NAME = "openai";
16
+ /** What a caller may name. Order is the order a reader sees them in the tool description. */
17
+ export declare const DRIVER_NAMES: readonly ["anthropic", "jev", "openai"];
18
+ /** An injected driver: not selectable, because only a caller in-process can supply one. */
19
+ export declare const CUSTOM_DRIVER_NAME = "custom";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The names of the drivers the harness can be asked for.
3
+ *
4
+ * A leaf module on purpose. These used to be re-exported from `harness-explore.ts`, which sits in
5
+ * the tool surface and is reached, through the toolset and the tool table, from the very tool that
6
+ * needs to declare them as an enum — a cycle that left the list `[undefined, undefined]` at the
7
+ * moment zod read it, and failed inside a JSON-schema parser rather than anywhere near the cause.
8
+ * Nothing here imports anything, so nothing can be half-initialised when it is read.
9
+ */
10
+ /** Generates its tool calls as text, and names the journeys it records. The default. */
11
+ export const ANTHROPIC_DRIVER_NAME = 'anthropic';
12
+ /** Answers typed questions; picks from the elements on the page and cannot invent one. */
13
+ export const JEV_DRIVER_NAME = 'jev';
14
+ /** Generates its tool calls, over Chat Completions. Here so an A/B has a third arm. */
15
+ export const OPENAI_DRIVER_NAME = 'openai';
16
+ /** What a caller may name. Order is the order a reader sees them in the tool description. */
17
+ export const DRIVER_NAMES = [ANTHROPIC_DRIVER_NAME, JEV_DRIVER_NAME, OPENAI_DRIVER_NAME];
18
+ /** An injected driver: not selectable, because only a caller in-process can supply one. */
19
+ export const CUSTOM_DRIVER_NAME = 'custom';
@@ -0,0 +1,67 @@
1
+ /**
2
+ * What to type into a field, and the one place in a drive worth paying a generating model for.
3
+ *
4
+ * A System One model answers typed questions against a state; it cannot write a string. That is not
5
+ * a shortcoming to route around everywhere — it is why the drive is two orders of magnitude cheaper,
6
+ * and every decision it makes (which tool, which element, what should follow) is a decision, not a
7
+ * composition. But a text field is a composition. "A card number", "a search term that returns
8
+ * results", "a description a human would write" cannot be enumerated from the page, and a label-keyed
9
+ * table of guesses is what stands in for them today.
10
+ *
11
+ * ## Why this is escalation rather than partnership
12
+ *
13
+ * Put a generating model in the per-turn loop and you pay its per-turn tax on every step, which is
14
+ * the cost the cheap driver exists to avoid. Typing is a small minority of what a drive does —
15
+ * clicks and navigations are the overwhelming majority — so the model is called for the few steps
16
+ * that compose text, never for the many that select, and a drive with no text fields never calls it
17
+ * at all.
18
+ *
19
+ * ## Why it is cached, and cached where it is
20
+ *
21
+ * A generated value is paid for ONCE and then belongs to the project: the same field on the same
22
+ * app wants the same value tomorrow, and a replay must send exactly what the recording sent or it
23
+ * is not a replay. Writing them to `.reticle` makes the second drive free, makes the values
24
+ * reviewable by a human who can correct a bad one by hand, and keeps them out of the model's way
25
+ * entirely. It is the same economics as the flows themselves: pay once, replay forever.
26
+ *
27
+ * ## What it must never do
28
+ *
29
+ * Fill a slot, never propose a call. The driver decides what to click and what to claim; this
30
+ * answers one question — "what text goes in this box" — and a wrong answer costs one act, not a
31
+ * wrong verdict. Nothing here touches what is judged.
32
+ */
33
+ import { type HarnessDriverOptions } from './driver.js';
34
+ /** What the caller must be able to do for values to survive past this drive. */
35
+ export interface FillCache {
36
+ get(label: string): string | undefined;
37
+ set(label: string, value: string): void;
38
+ }
39
+ /**
40
+ * The fallback, and still the answer for most fields.
41
+ *
42
+ * An exploration drive needs PLAUSIBLE input, not creative input: the engine is judging what the app
43
+ * did with the value, not the value. A field that rejects everything here is itself worth finding,
44
+ * and shows up as an act that did not settle.
45
+ */
46
+ export declare function heuristicFillValue(label: string): string;
47
+ /**
48
+ * Whether a label is worth spending a model call on.
49
+ *
50
+ * The heuristic is RIGHT about an email box and an amount box, and a generated "john@example.com"
51
+ * is not an improvement on `harness@reticle.dev`. What it cannot do is a field whose label it does
52
+ * not recognise at all, where it falls back to typing the words "reticle harness" into whatever the
53
+ * app asked for. That fallback is the whole of what generation is for.
54
+ */
55
+ export declare function needsGeneration(label: string): boolean;
56
+ /**
57
+ * The seam the driver calls: a label in, a value out, always.
58
+ *
59
+ * Three layers, cheapest first, and the expensive one is skipped entirely unless the two above it
60
+ * have nothing better than "type the words reticle harness into this box".
61
+ */
62
+ export declare function fillValues(deps: {
63
+ cache?: FillCache;
64
+ generator?: HarnessDriverOptions;
65
+ /** One line about where the drive is, so a value suits the page it is typed on. */
66
+ context?: () => string;
67
+ }): (label: string) => Promise<string>;
@@ -0,0 +1,153 @@
1
+ /**
2
+ * What to type into a field, and the one place in a drive worth paying a generating model for.
3
+ *
4
+ * A System One model answers typed questions against a state; it cannot write a string. That is not
5
+ * a shortcoming to route around everywhere — it is why the drive is two orders of magnitude cheaper,
6
+ * and every decision it makes (which tool, which element, what should follow) is a decision, not a
7
+ * composition. But a text field is a composition. "A card number", "a search term that returns
8
+ * results", "a description a human would write" cannot be enumerated from the page, and a label-keyed
9
+ * table of guesses is what stands in for them today.
10
+ *
11
+ * ## Why this is escalation rather than partnership
12
+ *
13
+ * Put a generating model in the per-turn loop and you pay its per-turn tax on every step, which is
14
+ * the cost the cheap driver exists to avoid. Typing is a small minority of what a drive does —
15
+ * clicks and navigations are the overwhelming majority — so the model is called for the few steps
16
+ * that compose text, never for the many that select, and a drive with no text fields never calls it
17
+ * at all.
18
+ *
19
+ * ## Why it is cached, and cached where it is
20
+ *
21
+ * A generated value is paid for ONCE and then belongs to the project: the same field on the same
22
+ * app wants the same value tomorrow, and a replay must send exactly what the recording sent or it
23
+ * is not a replay. Writing them to `.reticle` makes the second drive free, makes the values
24
+ * reviewable by a human who can correct a bad one by hand, and keeps them out of the model's way
25
+ * entirely. It is the same economics as the flows themselves: pay once, replay forever.
26
+ *
27
+ * ## What it must never do
28
+ *
29
+ * Fill a slot, never propose a call. The driver decides what to click and what to claim; this
30
+ * answers one question — "what text goes in this box" — and a wrong answer costs one act, not a
31
+ * wrong verdict. Nothing here touches what is judged.
32
+ */
33
+ import { DEFAULT_HARNESS_BASE_URL, } from './driver.js';
34
+ /** Deliberately small. A field value is a phrase; anything longer is the model misunderstanding. */
35
+ const MAX_TOKENS = 64;
36
+ const ANTHROPIC_VERSION = '2023-06-01';
37
+ const MESSAGES_PATH = '/v1/messages';
38
+ /**
39
+ * How many values one drive will generate.
40
+ *
41
+ * The cache makes the steady state nearly free, but the FIRST drive on a form-heavy app is the one
42
+ * that pays, and an uncapped loop on a page with forty inputs is a bill nobody agreed to. Past the
43
+ * cap the heuristic answers, which is what every drive used before this existed.
44
+ */
45
+ const MAX_PER_DRIVE = 12;
46
+ /**
47
+ * The fallback, and still the answer for most fields.
48
+ *
49
+ * An exploration drive needs PLAUSIBLE input, not creative input: the engine is judging what the app
50
+ * did with the value, not the value. A field that rejects everything here is itself worth finding,
51
+ * and shows up as an act that did not settle.
52
+ */
53
+ export function heuristicFillValue(label) {
54
+ const l = label.toLowerCase();
55
+ if (l.includes('email'))
56
+ return 'harness@reticle.dev';
57
+ if (l.includes('password'))
58
+ return 'password';
59
+ if (l.includes('url') || l.includes('link'))
60
+ return 'https://example.com';
61
+ if (l.includes('phone') || l.includes('tel'))
62
+ return '5550100';
63
+ if (l.includes('date'))
64
+ return '2026-01-01';
65
+ if (l.includes('amount') || l.includes('price') || l.includes('qty') || l.includes('quantity'))
66
+ return '2';
67
+ if (l.includes('search') || l.includes('filter'))
68
+ return 'a';
69
+ if (l.includes('number') || l.includes('count'))
70
+ return '42';
71
+ return 'reticle harness';
72
+ }
73
+ /**
74
+ * Whether a label is worth spending a model call on.
75
+ *
76
+ * The heuristic is RIGHT about an email box and an amount box, and a generated "john@example.com"
77
+ * is not an improvement on `harness@reticle.dev`. What it cannot do is a field whose label it does
78
+ * not recognise at all, where it falls back to typing the words "reticle harness" into whatever the
79
+ * app asked for. That fallback is the whole of what generation is for.
80
+ */
81
+ export function needsGeneration(label) {
82
+ return heuristicFillValue(label) === heuristicFillValue('');
83
+ }
84
+ const PROMPT = (label, context) => `A test harness is filling in a form field on a web application so it can check what the app does with the input.\n\n` +
85
+ `The field is labelled: "${label}"\n` +
86
+ `${context}\n\n` +
87
+ `Reply with ONLY the value to type. No quotes, no explanation, no markdown. It must be something a ` +
88
+ `real user would plausibly enter, and short. If the field wants an identifier or a code, invent a ` +
89
+ `well-formed one.`;
90
+ /** One string from a generating model, or nothing. Never throws: a drive must not fail over a field. */
91
+ async function generateOne(options, label, context) {
92
+ const call = options.fetch ?? ((url, init) => fetch(url, init));
93
+ const baseUrl = options.baseUrl ?? DEFAULT_HARNESS_BASE_URL;
94
+ try {
95
+ const res = await call(`${baseUrl}${MESSAGES_PATH}`, {
96
+ method: 'POST',
97
+ headers: {
98
+ 'content-type': 'application/json',
99
+ 'x-api-key': options.apiKey,
100
+ 'anthropic-version': ANTHROPIC_VERSION,
101
+ },
102
+ body: JSON.stringify({
103
+ model: options.model ?? 'claude-sonnet-5',
104
+ max_tokens: MAX_TOKENS,
105
+ messages: [{ role: 'user', content: PROMPT(label, context) }],
106
+ }),
107
+ });
108
+ if (!res.ok)
109
+ return undefined;
110
+ const parsed = JSON.parse(await res.text());
111
+ const content = parsed.content ?? [];
112
+ const text = content
113
+ .filter((block) => 'text' === block.type)
114
+ .map((block) => block.text ?? '')
115
+ .join('')
116
+ .trim()
117
+ .split('\n')[0];
118
+ if (text === undefined || 0 === text.length)
119
+ return undefined;
120
+ // A model asked for one value occasionally answers with a sentence about the value. Anything
121
+ // long enough to be prose is not a field value, and typing prose proves nothing about the app.
122
+ return 80 < text.length ? undefined : text.replace(/^["']|["']$/g, '');
123
+ }
124
+ catch {
125
+ // Offline, refused, rate-limited, malformed. All the same answer: use the heuristic.
126
+ return undefined;
127
+ }
128
+ }
129
+ /**
130
+ * The seam the driver calls: a label in, a value out, always.
131
+ *
132
+ * Three layers, cheapest first, and the expensive one is skipped entirely unless the two above it
133
+ * have nothing better than "type the words reticle harness into this box".
134
+ */
135
+ export function fillValues(deps) {
136
+ let spent = 0;
137
+ return async (label) => {
138
+ const cached = deps.cache?.get(label);
139
+ if (cached !== undefined)
140
+ return cached;
141
+ const fallback = heuristicFillValue(label);
142
+ if (deps.generator === undefined || !needsGeneration(label) || MAX_PER_DRIVE <= spent)
143
+ return fallback;
144
+ spent += 1;
145
+ const generated = await generateOne(deps.generator, label, deps.context?.() ?? '');
146
+ if (generated === undefined)
147
+ return fallback;
148
+ // Cached under the LABEL, not the ref: refs expire with the page, labels are what the next drive
149
+ // and every replay will meet again.
150
+ deps.cache?.set(label, generated);
151
+ return generated;
152
+ };
153
+ }