@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
@@ -7,8 +7,19 @@
7
7
  * nothing grades) but the flows that now exist on disk, because every future run replays those
8
8
  * deterministically with no model in the loop at all.
9
9
  */
10
- import { ReticleEnv } from '@reticlehq/core';
11
- import { harnessDriver, harnessOptionsFromEnv } from '../../features/harness/driver.js';
10
+ import { ReticleEnv, ReticleTool, apiKeyFrom, asRecord } from '@reticlehq/core';
11
+ import { projectForRoot } from '../../memory/project/project-for-root.js';
12
+ import { harnessDriver, harnessOptionsFromEnv, } from '../../features/harness/driver.js';
13
+ import { fillValues } from '../../features/harness/fill-values.js';
14
+ import { openFillValues } from '../../memory/project/dir/fill-value-store.js';
15
+ import { ANTHROPIC_DRIVER_NAME, CUSTOM_DRIVER_NAME, DRIVER_NAMES, JEV_DRIVER_NAME, OPENAI_DRIVER_NAME, } from '../../features/harness/drivers.js';
16
+ import { jevDriver, jevOptionsFromEnv } from '../../features/harness/jev-driver.js';
17
+ import { buildDomainModel } from '../../judgement/domain/domain-model.js';
18
+ import { readContract } from '../../memory/project/dir/reticle-dir.js';
19
+ import { sessionRoot } from '../../memory/project/session-root.js';
20
+ import { buildHarnessPlan, planAsText } from './harness-plan.js';
21
+ import { openAiDriver, openAiOptionsFromEnv } from '../../features/harness/openai-driver.js';
22
+ import { fetchPlatformConfig } from '../../features/harness/platform-config.js';
12
23
  import { DEFAULT_MAX_STEPS, runHarness, } from '../../features/harness/harness.js';
13
24
  import { reticleToolset } from './harness-toolset.js';
14
25
  /**
@@ -18,11 +29,60 @@ import { reticleToolset } from './harness-toolset.js';
18
29
  * the one thing to do about it. Named here because both the CLI and the honest no-flows refusal
19
30
  * print it, and two copies of this sentence would drift.
20
31
  */
21
- export const MSG_NO_HARNESS_KEY = `No model configured to drive the app: set ${ReticleEnv.HARNESS_KEY} to let Reticle explore it ` +
22
- `for you. Without it, flows are recorded by your own coding agent through the MCP tools.`;
32
+ export const MSG_NO_HARNESS_KEY = `No model configured to drive the app: set ${ReticleEnv.HARNESS_KEY}, or run \`reticle link\` and ` +
33
+ `set ${ReticleEnv.CLOUD_KEY}, to let Reticle explore it for you. Without either, flows are ` +
34
+ `recorded by your own coding agent through the MCP tools.`;
35
+ /** Asked for Jev specifically and it is not configured. Distinct from having no model at all. */
36
+ export const MSG_NO_JEV_KEY = `The \`${JEV_DRIVER_NAME}\` driver was asked for but is not configured: set ` +
37
+ `${ReticleEnv.HARNESS_JEV_KEY}, or run \`reticle link\` and set ${ReticleEnv.CLOUD_KEY}. ` +
38
+ `Refusing rather than quietly driving with another model, which would misattribute the result.`;
39
+ /** Asked for OpenAI specifically and it is not configured. Never substituted, same as Jev. */
40
+ export const MSG_NO_OPENAI_KEY = `The \`${OPENAI_DRIVER_NAME}\` driver was asked for but is not configured: set ` +
41
+ `${ReticleEnv.HARNESS_OPENAI_KEY}, or run \`reticle link\` and set ${ReticleEnv.CLOUD_KEY}. ` +
42
+ `Refusing rather than quietly driving with another model, which would misattribute the result.`;
43
+ const msgUnknownDriver = (asked) => `Unknown harness driver \`${asked}\`. Known drivers: ${DRIVER_NAMES.join(', ')}.`;
44
+ /**
45
+ * The environment, plus the credential this machine already has.
46
+ *
47
+ * `reticle link` mints a project-scoped key and files it in `~/.reticle/credentials.json`; the CLI
48
+ * has read it from there for as long as it has existed. The harness did not, and read only
49
+ * `process.env` — so somebody who had signed in, linked their project and been told they were
50
+ * connected still got "no model configured to drive the app", and the only way out was to find the
51
+ * key in the console and export it by hand. Five manual steps to reach a feature they had already
52
+ * finished setting up.
53
+ *
54
+ * Resolved into the env rather than threaded through four call sites: the drivers and the
55
+ * preference lookup all read an env record, and giving them a completed one leaves each of them
56
+ * exactly as simple as it was. An EXPLICIT variable still wins — someone who exported a key meant
57
+ * that key, and CI has no linked project to read.
58
+ *
59
+ * The credential arrives through `deps.linkedCloud`, a port, because resolving it here would make
60
+ * the tool surface reach into `memory/cloud` — a reach the directory guard refused, correctly.
61
+ */
62
+ export async function withLinkedCredential(deps, env) {
63
+ if (apiKeyFrom(env) !== undefined)
64
+ return env;
65
+ try {
66
+ const linked = await deps.linkedCloud?.();
67
+ if (linked === undefined || null === linked)
68
+ return env;
69
+ return {
70
+ ...env,
71
+ [ReticleEnv.API_KEY]: linked.apiKey,
72
+ [ReticleEnv.CLOUD_URL]: env[ReticleEnv.CLOUD_URL] ?? linked.url,
73
+ };
74
+ }
75
+ catch {
76
+ // A credential store that cannot be read is "not linked", not an error. The harness is optional
77
+ // and its absence is a routine answer; a drive must never fail because a JSON file was odd.
78
+ return env;
79
+ }
80
+ }
23
81
  /** Is there a model the harness can drive with? A read, because "not configured" is not a failure. */
24
82
  export function harnessAvailable(env) {
25
- return harnessOptionsFromEnv(env) !== undefined;
83
+ return (harnessOptionsFromEnv(env) !== undefined ||
84
+ jevOptionsFromEnv(env) !== undefined ||
85
+ openAiOptionsFromEnv(env) !== undefined);
26
86
  }
27
87
  /**
28
88
  * Read the step ceiling out of the environment.
@@ -47,21 +107,347 @@ export function maxStepsFromEnv(env) {
47
107
  * record and did not — and this product does not get to make that mistake about its own work.
48
108
  */
49
109
  export async function exploreApp(deps, env, options = {}) {
50
- const driver = options.driver ?? buildDriver(env);
110
+ const refusal = await refusedByPlatform(env, options);
111
+ if (refusal !== undefined)
112
+ throw new Error(refusal);
113
+ const maxSteps = options.maxSteps ?? maxStepsFromEnv(env);
51
114
  const before = new Set(await deps.flows.list());
52
- const drive = await runHarness(driver, reticleToolset(deps, pinned(options)), {
53
- maxSteps: options.maxSteps ?? maxStepsFromEnv(env),
54
- ...(options.focus === undefined ? {} : { focus: options.focus }),
115
+ // `.reticle` FIRST, before the app and before anything reads a line of source. It already holds
116
+ // every saved flow, the consequence that must hold for each, and the declared intent nobody has
117
+ // tested — which is the whole of what a drive should be deciding against.
118
+ const plan = await readPlan(deps, options.sessionId);
119
+ /*
120
+ * The fixtures this project has already paid for.
121
+ *
122
+ * A drive is a sequence of DECISIONS and, occasionally, one COMPOSITION: what to type into a box
123
+ * whose label nothing can guess from. The cheap driver cannot write a string at all, so those
124
+ * boxes used to receive the words "reticle harness". This is the seam where a generating model
125
+ * answers instead -- for about 6% of steps, once per label, and never again on any later drive,
126
+ * because the answer is written into `.reticle` beside the flows that use it.
127
+ */
128
+ const fills = await openFillValues(deps.fs, safeRoot(deps, options.sessionId));
129
+ const requested = options.driverName ?? env[ReticleEnv.HARNESS_DRIVER] ?? (await preferredDriver(env, options));
130
+ const built = options.driver === undefined
131
+ ? buildDriver(env, maxSteps, plan, requested, fills)
132
+ : { driver: options.driver, name: CUSTOM_DRIVER_NAME };
133
+ const driver = built.driver;
134
+ const toolset = reticleToolset(deps, pinned(options));
135
+ const drive = await runHarness(driver, toolset, {
136
+ maxSteps,
137
+ // The plan rides in as standing instruction, so it is in front of the model on every turn
138
+ // rather than remembered from a first one. `focus` is the caller's own words and goes last:
139
+ // somebody who named a journey meant that journey, whatever the project's backlog says.
140
+ focus: [
141
+ planAsText(plan),
142
+ ...(options.focus === undefined ? [] : [`Focus: ${options.focus}`]),
143
+ ].join('\n\n'),
55
144
  });
145
+ // Written once, after the drive, whatever the drive did: a run that broke still learned what it
146
+ // learned, and the next one should not pay for it again.
147
+ await fills.flush();
148
+ // MANDATORY, and deliberately outside the loop. A drive that runs out of budget mid-journey, or
149
+ // breaks, or whose model simply stops asking for tools, leaves a recording open and everything it
150
+ // drove unsaved — work paid for and thrown away. Saving is not a decision any model gets to make
151
+ // and not something a step budget gets to cut off, so it happens here, after the loop, always.
152
+ await bankOpenRecording(toolset, drive);
56
153
  const after = await deps.flows.list();
57
- return { drive, savedFlows: after.filter((name) => !before.has(name)) };
154
+ return { drive, plan, driverName: built.name, ...reconcileFlows(before, after, drive.toolCalls) };
155
+ }
156
+ /**
157
+ * What a drive actually left behind, split into flows that are NEW and flows it wrote over.
158
+ *
159
+ * Pure, and exported, because it is the part that was wrong and the part worth pinning. The
160
+ * integration around it — a real toolset dispatching a real save — is covered by driving a real app;
161
+ * what a test can usefully hold still is the reconciliation itself.
162
+ *
163
+ * A rewrite is only counted when the drive claims the save AND the store lists the name afterwards.
164
+ * The claim on its own is not evidence, which is the rule this function existed to keep: a model
165
+ * that says it saved something it did not must not be believed. Requiring both is what lets a
166
+ * rewrite be seen at all, since a name that was already there cannot show up in a before/after diff.
167
+ */
168
+ export function reconcileFlows(before, after, toolCalls) {
169
+ const present = new Set(after);
170
+ const claimed = toolCalls
171
+ .filter((call) => ReticleTool.FLOW_SAVE === call.name && !call.isError)
172
+ .map((call) => {
173
+ // The flow's filename is `saveAs` when given, and the recording's name otherwise. Read from
174
+ // the result first, which is what the store actually wrote, and fall back to the request.
175
+ const result = asRecord(call.result)['name'];
176
+ if ('string' === typeof result)
177
+ return result;
178
+ const args = asRecord(call.args);
179
+ const saveAs = args['saveAs'];
180
+ const flowName = args['flowName'];
181
+ if ('string' === typeof saveAs)
182
+ return saveAs;
183
+ return 'string' === typeof flowName ? flowName : undefined;
184
+ })
185
+ .filter((name) => name !== undefined && present.has(name));
186
+ return {
187
+ savedFlows: after.filter((name) => !before.has(name)),
188
+ rewroteFlows: [...new Set(claimed.filter((name) => before.has(name)))],
189
+ };
190
+ }
191
+ /**
192
+ * The driver this project chose on the platform, if it chose one, we can reach it, and we have it.
193
+ *
194
+ * Only asked when nobody has been explicit, so the cost is paid exactly once per drive and never on
195
+ * a path where it could not change the answer. An unreachable platform returns undefined and the
196
+ * environment decides, exactly as it did before this lookup existed.
197
+ *
198
+ * A preference naming a driver this build does not have is IGNORED rather than refused, and that
199
+ * asymmetry with the per-call argument is deliberate. The platform offers providers this daemon may
200
+ * be too old to know about — it already offers `openai`, which has no binding here — so a stored
201
+ * preference is a statement about the account, not an instruction for this drive, and a daemon that
202
+ * refused to run because a web UI knew one more word than it does would be broken by its own
203
+ * upgrade cycle. Naming a driver in the CALL is an instruction, and an unknown one is still an
204
+ * error there.
205
+ *
206
+ * It is not a silent substitution either way: the result reports the driver that actually drove.
207
+ */
208
+ async function preferredDriver(env, options) {
209
+ if (true === options.skipPlatformConfig)
210
+ return undefined;
211
+ return knownDriver((await platformConfig(env, options))?.provider);
212
+ }
213
+ /**
214
+ * What a person said about autonomous driving on the platform, honoured here.
215
+ *
216
+ * The switch existed, persisted and round-tripped, and the daemon read it and threw it away — so
217
+ * turning the harness OFF changed a value in a database and nothing else. A control that does not
218
+ * control anything is worse than no control: somebody turns it off, watches Reticle drive their app
219
+ * anyway, and now correctly distrusts every other switch in the product.
220
+ *
221
+ * Absent means ON. The platform's own default is on, and a machine that cannot reach the platform —
222
+ * offline, CI, no link — must not silently lose a feature it was never told to stop using.
223
+ */
224
+ export const MSG_HARNESS_DISABLED = 'Autonomous driving is turned OFF for this project. Turn it back on in the Reticle dashboard ' +
225
+ '(Settings → Verification model), or drive the app yourself through the MCP tools.';
226
+ /**
227
+ * The other reason a drive can be refused, and it is NOT the same reason.
228
+ *
229
+ * A drive through the platform spends Reticle's model budget. That is free for three months and
230
+ * included on a paid plan, and outside both it is somebody else's money being spent on nothing. The
231
+ * message says which of the two it is and what to do, because "the harness is off" told to a person
232
+ * who never turned anything off is a support ticket rather than an answer.
233
+ */
234
+ export const MSG_HARNESS_UNCLAIMED = 'This workspace has no harness entitlement, so autonomous driving would run on Reticle’s model ' +
235
+ 'budget with nothing paying for it. Claim the free 3 months in the Reticle dashboard, or export a ' +
236
+ 'model API key of your own and drive with that.';
237
+ /**
238
+ * Whether a drive would be paid for by the person asking for it.
239
+ *
240
+ * Entitlement gates OUR spend, so it has no business stopping somebody who brought their own key:
241
+ * their harness costs us nothing whether they ever claim anything or not. Reading the same options
242
+ * the drivers read keeps the two answers from drifting apart.
243
+ */
244
+ function ownsAModelKey(env) {
245
+ // The DIRECT variables only. Every driver also accepts the platform key as a fallback, so asking
246
+ // `jevOptionsFromEnv` here would answer "they have a key" for exactly the person whose drive would
247
+ // be billed to us — which is the one case this gate exists for.
248
+ return [ReticleEnv.HARNESS_KEY, ReticleEnv.HARNESS_JEV_KEY, ReticleEnv.HARNESS_OPENAI_KEY].some((name) => {
249
+ const value = env[name];
250
+ return value !== undefined && 0 < value.length;
251
+ });
252
+ }
253
+ /** One read, however many questions are asked of the answer. */
254
+ function platformConfig(env, options) {
255
+ return options.configFetch === undefined
256
+ ? fetchPlatformConfig(env)
257
+ : fetchPlatformConfig(env, options.configFetch);
258
+ }
259
+ /**
260
+ * The reason this drive must not start, or `undefined` to go ahead.
261
+ *
262
+ * It FAILS OPEN, and that is worth stating plainly because the argument for it changed under it.
263
+ * `fetchPlatformConfig` answers `undefined` on a network error, a non-2xx, a body that does not
264
+ * parse, or a two-second timeout, and all four land here as "carry on". When this endpoint only
265
+ * answered "which model does this project prefer", failing open cost nothing: the drive used the
266
+ * environment and nobody was worse off. It now also answers "is anybody paying for this", and the
267
+ * same silence means a drive runs on Reticle's model budget unmetered.
268
+ *
269
+ * Kept open anyway, deliberately: a settings endpoint having a slow second must not break somebody
270
+ * mid-verification, and the exposure is one drive's worth of a very cheap model. But this is
271
+ * best-effort, not enforcement -- if the spend needs a hard floor, it belongs at the proxy, which
272
+ * is the only side that cannot be talked out of it by a dropped packet.
273
+ */
274
+ async function refusedByPlatform(env, options) {
275
+ if (true === options.skipPlatformConfig)
276
+ return undefined;
277
+ const config = await platformConfig(env, options);
278
+ if (config === undefined)
279
+ return undefined;
280
+ if (!config.harnessEnabled)
281
+ return MSG_HARNESS_DISABLED;
282
+ if (!config.harnessEntitled && !ownsAModelKey(env))
283
+ return MSG_HARNESS_UNCLAIMED;
284
+ return undefined;
285
+ }
286
+ /**
287
+ * A stored preference, kept only if this build can actually honour it.
288
+ *
289
+ * Exported because it is the whole of the asymmetry with the per-call argument, and the asymmetry
290
+ * is the part somebody will later think is a bug.
291
+ */
292
+ export function knownDriver(provider) {
293
+ if (provider === undefined)
294
+ return undefined;
295
+ return DRIVER_NAMES.some((name) => name === provider) ? provider : undefined;
296
+ }
297
+ /**
298
+ * Close and save whatever recording the drive left open.
299
+ *
300
+ * The driver reserves turns for its own teardown, which covers the ordinary ending. It does NOT
301
+ * cover a drive that broke, or one whose model went quiet, or one cut off by a budget the caller
302
+ * shortened — and in every one of those the app really was driven and the record of it is thrown
303
+ * away. Measured before this existed: a 24-step journey through a real dashboard, saved nothing.
304
+ *
305
+ * Failures are swallowed on purpose. This is a last chance, not a checkpoint: if the recording was
306
+ * already closed the stop is refused and there is nothing to do, and a drive must not fail at the
307
+ * finish line because the thing it was trying to rescue did not need rescuing.
308
+ */
309
+ async function bankOpenRecording(toolset, drive) {
310
+ const open = openRecordingName(drive.toolCalls);
311
+ if (open === undefined)
312
+ return;
313
+ try {
314
+ await toolset.invoke(ReticleTool.RECORD, { action: 'stop', recordingName: open });
315
+ await toolset.invoke(ReticleTool.FLOW_SAVE, {
316
+ flowName: open,
317
+ intent: `Autonomous drive of ${open}, banked after the run ended.`,
318
+ });
319
+ }
320
+ catch {
321
+ /* last chance, not a checkpoint */
322
+ }
323
+ }
324
+ /** The recording started and never stopped, if there is one. Exported because it is the decision. */
325
+ export function openRecordingName(toolCalls) {
326
+ let open;
327
+ for (const call of toolCalls) {
328
+ if (ReticleTool.RECORD !== call.name)
329
+ continue;
330
+ const args = asRecord(call.args);
331
+ const name = args['recordingName'];
332
+ if ('string' !== typeof name)
333
+ continue;
334
+ if ('start' === args['action'] && !call.isError)
335
+ open = name;
336
+ if ('stop' === args['action'] && name === open)
337
+ open = undefined;
338
+ }
339
+ return open;
340
+ }
341
+ /**
342
+ * Read the project's own record of what it does and what is proved about it.
343
+ *
344
+ * No browser, no model, no source. A failure here is "nothing is recorded yet", which is the
345
+ * ordinary state of a new project and not a reason to refuse to drive — so it answers an empty
346
+ * plan rather than throwing.
347
+ */
348
+ async function readPlan(deps, sessionId) {
349
+ try {
350
+ const flows = [];
351
+ for (const name of await deps.flows.list()) {
352
+ const loaded = await deps.flows.load(name);
353
+ if (loaded.ok)
354
+ flows.push(loaded.value);
355
+ }
356
+ const root = sessionRoot(deps, sessionId);
357
+ const contract = await readContract(deps.fs, root);
358
+ const project = await projectForRoot(deps, root).read();
359
+ return buildHarnessPlan(buildDomainModel(flows, contract.ok ? contract.capabilities : null, project.ok ? project.file.runs : []));
360
+ }
361
+ catch {
362
+ return {
363
+ steps: [],
364
+ summary: 'The project record could not be read; driving without a plan.',
365
+ vocabulary: [],
366
+ };
367
+ }
368
+ }
369
+ /**
370
+ * The project directory, or nothing.
371
+ *
372
+ * `sessionRoot` needs a session and a project to answer, and a drive can legitimately be asked for
373
+ * before either is resolvable. Everything else that reads `.reticle` here already treats that as
374
+ * "no project record" rather than as a failure; the fixtures file is no different, and a throw on
375
+ * this path would turn a missing directory into a refused drive.
376
+ */
377
+ function safeRoot(deps, sessionId) {
378
+ try {
379
+ return sessionRoot(deps, sessionId);
380
+ }
381
+ catch {
382
+ return undefined;
383
+ }
58
384
  }
59
385
  function pinned(options) {
60
386
  return options.sessionId === undefined ? {} : { sessionId: options.sessionId };
61
387
  }
62
- function buildDriver(env) {
63
- const configured = harnessOptionsFromEnv(env);
64
- if (configured === undefined)
65
- throw new Error(MSG_NO_HARNESS_KEY);
66
- return harnessDriver(configured);
388
+ /**
389
+ * Pick the driver.
390
+ *
391
+ * The Anthropic driver stays the default, and an explicit `RETICLE_HARNESS_DRIVER=jev` is the only
392
+ * thing that moves off it when both are configured — a driver change is a change in how the app gets
393
+ * driven, and inferring one from which key happens to be exported would swap it under people who
394
+ * merely linked their account.
395
+ *
396
+ * The fall-through is the other direction and is not a preference: with no Anthropic key and a
397
+ * platform key present, Jev is not the cheaper option, it is the only one. Choosing it there is what
398
+ * makes the harness work for somebody who never had a model API key of their own, which is most
399
+ * people — and the reason this driver is worth having at all.
400
+ */
401
+ function buildDriver(env, maxSteps, plan, requested, fills) {
402
+ // The two halves of what `.reticle` knows, handed to the one driver that can use both: what is
403
+ // worth doing, and the names the app uses for what it does. The second is what lets a declared
404
+ // consequence be SAVED rather than merely proved; see `consequencesFor`.
405
+ const fromReticle = {
406
+ plan: plan.steps,
407
+ vocabulary: plan.vocabulary,
408
+ /*
409
+ * Generation is offered ONLY when a generating model is already configured, and it stays out of
410
+ * the per-turn loop either way: put a text model on every turn and you pay its per-turn tax on
411
+ * the 94% of steps that are clicks, which is the entire cost the cheap driver exists to avoid.
412
+ * With no such key the label heuristic answers, exactly as it did before this existed.
413
+ */
414
+ fillValue: fillValues({
415
+ ...(fills === undefined ? {} : { cache: fills }),
416
+ ...(harnessOptionsFromEnv(env) === undefined
417
+ ? {}
418
+ : { generator: harnessOptionsFromEnv(env) }),
419
+ }),
420
+ };
421
+ const jev = jevOptionsFromEnv(env);
422
+ const anthropic = harnessOptionsFromEnv(env);
423
+ const asked = requested;
424
+ // A driver that was ASKED for and is not configured is an error, never a substitution. Quietly
425
+ // falling back would make every comparison between two drivers a possible lie about which one
426
+ // produced the result — and comparing them is the main reason anyone names one.
427
+ if (JEV_DRIVER_NAME === asked) {
428
+ if (jev === undefined)
429
+ throw new Error(MSG_NO_JEV_KEY);
430
+ // The Jev driver is told the budget because it has a teardown to reach; the Anthropic driver is
431
+ // not, because it calls `finish` itself and being handed a number it did not ask for is how a
432
+ // second copy of the budget starts drifting from the loop's.
433
+ return { driver: jevDriver({ ...jev, maxSteps, ...fromReticle }), name: JEV_DRIVER_NAME };
434
+ }
435
+ if (ANTHROPIC_DRIVER_NAME === asked) {
436
+ if (anthropic === undefined)
437
+ throw new Error(MSG_NO_HARNESS_KEY);
438
+ return { driver: harnessDriver(anthropic), name: ANTHROPIC_DRIVER_NAME };
439
+ }
440
+ if (OPENAI_DRIVER_NAME === asked) {
441
+ const openai = openAiOptionsFromEnv(env);
442
+ if (openai === undefined)
443
+ throw new Error(MSG_NO_OPENAI_KEY);
444
+ return { driver: openAiDriver(openai), name: OPENAI_DRIVER_NAME };
445
+ }
446
+ if (asked !== undefined && 0 < asked.length)
447
+ throw new Error(msgUnknownDriver(asked));
448
+ if (anthropic !== undefined)
449
+ return { driver: harnessDriver(anthropic), name: ANTHROPIC_DRIVER_NAME };
450
+ if (jev !== undefined)
451
+ return { driver: jevDriver({ ...jev, maxSteps, ...fromReticle }), name: JEV_DRIVER_NAME };
452
+ throw new Error(MSG_NO_HARNESS_KEY);
67
453
  }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * What a drive is FOR, decided before it starts and read out of `.reticle` rather than the app.
3
+ *
4
+ * The driver used to open its eyes on a page and pick whatever looked interesting. That makes every
5
+ * run start from zero: it re-drives journeys that are already saved, and it has no idea which
6
+ * declared intent nobody has ever tested. Both are the same missing input — the project's own
7
+ * record of what it claims to do and what has been proved about it.
8
+ *
9
+ * `.reticle` already holds that. `buildDomainModel` reads `.reticle/flows/` and
10
+ * `.reticle/contract.json` and answers with every saved flow, the consequence that MUST hold for
11
+ * each one, and the GAPS: signals and testids the app DECLARED that no flow asserts. So the plan is
12
+ * derivable with no browser, no model and no source code — which is the order this product argues
13
+ * for everywhere else and did not follow here.
14
+ *
15
+ * Two kinds of step, and the split is the whole economics:
16
+ *
17
+ * REPLAY a journey that is already recorded. Deterministic, no model in the loop, a few hundred
18
+ * tokens. Anything already covered belongs here and must never be re-driven.
19
+ * DRIVE a gap. A model is worth paying for exactly where nothing is proved yet.
20
+ *
21
+ * Nothing here calls a model. The plan is the deterministic half, and the model's only job is to
22
+ * choose among steps somebody already decided were worth taking.
23
+ */
24
+ import type { DomainModel } from '../../judgement/domain/domain-model.js';
25
+ export declare const PlanStepKind: {
26
+ /** Already recorded: replay it deterministically instead of paying a model to rediscover it. */
27
+ readonly REPLAY: "replay";
28
+ /** Declared but never proved: this is what a drive is actually for. */
29
+ readonly DRIVE: "drive";
30
+ };
31
+ export type PlanStepKind = (typeof PlanStepKind)[keyof typeof PlanStepKind];
32
+ export interface PlanStep {
33
+ kind: PlanStepKind;
34
+ /** The flow to replay, or a short label for the gap to cover. */
35
+ target: string;
36
+ /**
37
+ * WHY this step is in the plan, in the project's own words.
38
+ *
39
+ * For a replay it is the consequence that must hold — the flow's `mustHold`, which is the thing
40
+ * that stops being true when the feature breaks. For a drive it is the declared intent nobody has
41
+ * tested. Carried because the model choosing between steps should be reading intent, not a list
42
+ * of names it has no way to rank.
43
+ */
44
+ why: string;
45
+ }
46
+ export interface HarnessPlan {
47
+ steps: readonly PlanStep[];
48
+ /** One line an agent or a human can read, derived rather than narrated. */
49
+ summary: string;
50
+ /**
51
+ * The app's OWN vocabulary: the signals it declared in its capabilities, untested ones first.
52
+ *
53
+ * Carried out of `.reticle` for the same reason the steps are, and then used for something the
54
+ * plan itself never does. A drive declares what it expects BEFORE it acts, and a declaration can
55
+ * only be saved into the flow if it names something: `signal "order:placed"` records, a bare
56
+ * "some signal fires" does not, and a flow that records nothing replays green whatever the
57
+ * feature does. These are the names that make the difference.
58
+ */
59
+ vocabulary: readonly string[];
60
+ }
61
+ /**
62
+ * Build the plan.
63
+ *
64
+ * Replays come first, always. They are the cheap half and they establish what is still true before
65
+ * any model is paid to explore what is not yet known — and a regression found by a replay is found
66
+ * for a few hundred tokens rather than a few hundred thousand.
67
+ */
68
+ export declare function buildHarnessPlan(domain: DomainModel): HarnessPlan;
69
+ /**
70
+ * The plan as the model reads it.
71
+ *
72
+ * Intent first and names second, because a model ranking "what next" against a list of identifiers
73
+ * is guessing, and ranking against what the project says each one is FOR is not. The kind is stated
74
+ * so it can prefer finishing the cheap deterministic half before spending itself on the rest.
75
+ */
76
+ export declare function planAsText(plan: HarnessPlan): string;