@reticlehq/server 3.5.0 → 3.7.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 (721) hide show
  1. package/README.md +1 -1
  2. package/SKILL.md +123 -116
  3. package/dist/chromium-launch-options.d.ts +3 -1
  4. package/dist/chromium-launch-options.js +2 -1
  5. package/dist/command/cli/adhoc-suite.d.ts +14 -0
  6. package/dist/command/cli/adhoc-suite.js +42 -7
  7. package/dist/command/cli/adhoc-verdict.d.ts +19 -2
  8. package/dist/command/cli/adhoc-verdict.js +76 -9
  9. package/dist/command/cli/answers/gate-exit.d.ts +23 -0
  10. package/dist/command/cli/answers/gate-exit.js +45 -0
  11. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  12. package/dist/command/cli/answers/gate-hook-message.js +3 -1
  13. package/dist/command/cli/answers/open-note.d.ts +2 -0
  14. package/dist/command/cli/answers/open-note.js +22 -0
  15. package/dist/command/cli/auth/device-flow.d.ts +13 -2
  16. package/dist/command/cli/auth/device-flow.js +17 -0
  17. package/dist/command/cli/cli-doctor.js +14 -0
  18. package/dist/command/cli/cli-flow-commands.d.ts +0 -11
  19. package/dist/command/cli/cli-flow-commands.js +31 -112
  20. package/dist/command/cli/cli-kill.js +3 -2
  21. package/dist/command/cli/cli-parse-grammar.d.ts +24 -0
  22. package/dist/command/cli/cli-parse-grammar.js +75 -0
  23. package/dist/command/cli/cli-parse-verify.d.ts +2 -0
  24. package/dist/command/cli/cli-parse-verify.js +18 -0
  25. package/dist/command/cli/cli-parse.d.ts +19 -28
  26. package/dist/command/cli/cli-parse.js +82 -182
  27. package/dist/command/cli/cli-update-commands.d.ts +1 -1
  28. package/dist/command/cli/cli-update-commands.js +61 -9
  29. package/dist/command/cli/cli-usage.d.ts +33 -0
  30. package/dist/command/cli/cli-usage.js +287 -0
  31. package/dist/command/cli/cli-verify.d.ts +35 -5
  32. package/dist/command/cli/cli-verify.js +114 -35
  33. package/dist/command/cli/cloud-cli.d.ts +13 -0
  34. package/dist/command/cli/cloud-cli.js +192 -341
  35. package/dist/command/cli/cloud-kit.d.ts +12 -0
  36. package/dist/command/cli/cloud-kit.js +41 -2
  37. package/dist/command/cli/cloud-link.d.ts +57 -0
  38. package/dist/command/cli/cloud-link.js +308 -0
  39. package/dist/command/cli/cloud-login.d.ts +22 -0
  40. package/dist/command/cli/cloud-login.js +96 -8
  41. package/dist/command/cli/context-command.d.ts +1 -0
  42. package/dist/command/cli/context-command.js +33 -0
  43. package/dist/command/cli/daemon-start-options.d.ts +3 -0
  44. package/dist/command/cli/daemon-start-options.js +24 -3
  45. package/dist/command/cli/doctor/browser/chromium-hint.d.ts +46 -0
  46. package/dist/command/cli/doctor/browser/chromium-hint.js +99 -1
  47. package/dist/command/cli/doctor/doctor-coverage-line.d.ts +2 -0
  48. package/dist/command/cli/doctor/doctor-coverage-line.js +47 -0
  49. package/dist/command/cli/doctor/doctor-project-line.d.ts +0 -12
  50. package/dist/command/cli/doctor/doctor-project-line.js +5 -4
  51. package/dist/command/cli/doctor/doctor-rows.d.ts +6 -0
  52. package/dist/command/cli/doctor/doctor-rows.js +6 -0
  53. package/dist/command/cli/doctor/doctor-signing-line.d.ts +38 -0
  54. package/dist/command/cli/doctor/doctor-signing-line.js +114 -0
  55. package/dist/command/cli/drive/drive-attach.d.ts +27 -2
  56. package/dist/command/cli/drive/drive-attach.js +71 -4
  57. package/dist/command/cli/drive/drive-command.d.ts +2 -1
  58. package/dist/command/cli/drive/drive-command.js +13 -7
  59. package/dist/command/cli/known-regression.d.ts +13 -0
  60. package/dist/command/cli/known-regression.js +34 -0
  61. package/dist/command/cli/launch/sdk-version-match.d.ts +12 -0
  62. package/dist/command/cli/launch/sdk-version-match.js +25 -0
  63. package/dist/command/cli/lifecycle/daemon-lifecycle.js +15 -3
  64. package/dist/command/cli/mcp-command.d.ts +6 -0
  65. package/dist/command/cli/mcp-command.js +13 -5
  66. package/dist/command/cli/ports/port-holder.d.ts +10 -0
  67. package/dist/command/cli/ports/port-holder.js +25 -1
  68. package/dist/command/cli/project-name.d.ts +29 -1
  69. package/dist/command/cli/project-name.js +107 -2
  70. package/dist/command/cli/setup-mcp-cli.d.ts +17 -1
  71. package/dist/command/cli/setup-mcp-cli.js +35 -12
  72. package/dist/command/cli/status/status-lines.d.ts +1 -0
  73. package/dist/command/cli/status/status-lines.js +47 -2
  74. package/dist/command/cli/status/workspace-links.d.ts +10 -0
  75. package/dist/command/cli/status/workspace-links.js +29 -0
  76. package/dist/command/cli/try-command.d.ts +96 -0
  77. package/dist/command/cli/try-command.js +362 -0
  78. package/dist/command/cli/tutorial.d.ts +0 -100
  79. package/dist/command/cli/tutorial.js +0 -181
  80. package/dist/command/cli/verify-results.d.ts +19 -0
  81. package/dist/command/cli/verify-results.js +29 -0
  82. package/dist/command/cli-onboarding.d.ts +0 -11
  83. package/dist/command/cli-onboarding.js +0 -15
  84. package/dist/command/cli.d.ts +20 -0
  85. package/dist/command/cli.js +130 -104
  86. package/dist/command/daemon/binding/daemon-status-probe.d.ts +2 -0
  87. package/dist/command/daemon/binding/daemon-status-probe.js +1 -0
  88. package/dist/command/daemon/daemon-resilience.js +4 -4
  89. package/dist/command/daemon/daemon-resolve.d.ts +23 -1
  90. package/dist/command/daemon/daemon-resolve.js +79 -9
  91. package/dist/command/setup/agent-writer.d.ts +1 -1
  92. package/dist/command/setup/agent-writer.js +3 -2
  93. package/dist/command/setup/bringup/bridge-port.d.ts +3 -1
  94. package/dist/command/setup/bringup/bridge-port.js +13 -3
  95. package/dist/command/setup/bringup/connect-lease.d.ts +1 -0
  96. package/dist/command/setup/bringup/connect-lease.js +1 -0
  97. package/dist/command/setup/bringup/dev-server-log-cap.js +2 -0
  98. package/dist/command/setup/bringup/dev-server-wait.d.ts +8 -0
  99. package/dist/command/setup/bringup/dev-server-wait.js +19 -0
  100. package/dist/command/setup/bringup/ensure-daemon.js +9 -2
  101. package/dist/command/setup/init/agent-restart.d.ts +23 -0
  102. package/dist/command/setup/init/agent-restart.js +28 -0
  103. package/dist/command/setup/init/init-port.d.ts +14 -1
  104. package/dist/command/setup/init/init-port.js +32 -2
  105. package/dist/command/setup/init/init-runtime.d.ts +22 -2
  106. package/dist/command/setup/init/init-runtime.js +114 -38
  107. package/dist/command/setup/node-effects.d.ts +2 -0
  108. package/dist/command/setup/node-effects.js +38 -9
  109. package/dist/command/setup/probe/existing-app.d.ts +12 -0
  110. package/dist/command/setup/probe/existing-app.js +14 -0
  111. package/dist/command/setup/remaining-steps.js +2 -1
  112. package/dist/command/setup/run-setup.d.ts +34 -9
  113. package/dist/command/setup/run-setup.js +65 -51
  114. package/dist/command/setup/setup-command.d.ts +25 -1
  115. package/dist/command/setup/setup-command.js +59 -4
  116. package/dist/command/setup/setup-mcp.js +25 -2
  117. package/dist/command/setup/tauri-signing.d.ts +19 -0
  118. package/dist/command/setup/tauri-signing.js +79 -0
  119. package/dist/command/setup/terminal/listeners.d.ts +10 -1
  120. package/dist/command/setup/terminal/listeners.js +37 -6
  121. package/dist/command/update/update-nudge.d.ts +9 -0
  122. package/dist/command/update/update-nudge.js +1 -1
  123. package/dist/command/update/updater.js +11 -15
  124. package/dist/command/version/version-nudge.js +3 -0
  125. package/dist/command/version/version-skew.d.ts +20 -1
  126. package/dist/command/version/version-skew.js +49 -4
  127. package/dist/features/crawl/crawl-tools.js +4 -0
  128. package/dist/features/crawl/crawl.d.ts +6 -0
  129. package/dist/features/crawl/crawl.js +23 -2
  130. package/dist/features/exhaust/ledger.d.ts +21 -14
  131. package/dist/features/exhaust/ledger.js +35 -9
  132. package/dist/features/harness/app-findings.d.ts +2 -0
  133. package/dist/features/harness/app-findings.js +52 -0
  134. package/dist/features/harness/drive-report.d.ts +61 -1
  135. package/dist/features/harness/drive-report.js +394 -26
  136. package/dist/features/harness/drive-runs.d.ts +120 -0
  137. package/dist/features/harness/drive-runs.js +289 -0
  138. package/dist/features/harness/drivers.d.ts +10 -14
  139. package/dist/features/harness/drivers.js +10 -14
  140. package/dist/features/harness/explore/console-pages.d.ts +15 -0
  141. package/dist/features/harness/explore/console-pages.js +146 -0
  142. package/dist/features/harness/explore/disparity.d.ts +19 -0
  143. package/dist/features/harness/explore/disparity.js +71 -0
  144. package/dist/features/harness/explore/fake-app.d.ts +16 -0
  145. package/dist/features/harness/explore/fake-app.js +49 -0
  146. package/dist/features/harness/explore/goal-search.d.ts +26 -0
  147. package/dist/features/harness/explore/goal-search.js +144 -0
  148. package/dist/features/harness/explore/page-tree.d.ts +49 -0
  149. package/dist/features/harness/explore/page-tree.js +148 -0
  150. package/dist/features/harness/explore/site-map.d.ts +34 -0
  151. package/dist/features/harness/explore/site-map.js +150 -0
  152. package/dist/features/harness/fill-values.d.ts +3 -73
  153. package/dist/features/harness/fill-values.js +3 -169
  154. package/dist/features/harness/goals.d.ts +82 -0
  155. package/dist/features/harness/goals.js +330 -0
  156. package/dist/features/harness/harness.d.ts +51 -1
  157. package/dist/features/harness/harness.js +207 -6
  158. package/dist/features/harness/platform/drive-target.d.ts +30 -0
  159. package/dist/features/harness/platform/drive-target.js +81 -0
  160. package/dist/features/harness/platform/local-apps.d.ts +127 -0
  161. package/dist/features/harness/platform/local-apps.js +280 -0
  162. package/dist/features/harness/platform/persona-credentials.d.ts +20 -0
  163. package/dist/features/harness/platform/persona-credentials.js +69 -0
  164. package/dist/features/harness/platform/platform-drives.d.ts +61 -0
  165. package/dist/features/harness/platform/platform-drives.js +114 -0
  166. package/dist/features/harness/platform/refusal.d.ts +52 -0
  167. package/dist/features/harness/platform/refusal.js +71 -0
  168. package/dist/features/harness/platform/remote-drive.d.ts +186 -0
  169. package/dist/features/harness/platform/remote-drive.js +378 -0
  170. package/dist/features/harness/platform/script.d.ts +94 -0
  171. package/dist/features/harness/platform/script.js +260 -0
  172. package/dist/features/harness/platform/server-driver.d.ts +54 -0
  173. package/dist/features/harness/platform/server-driver.js +182 -0
  174. package/dist/features/harness/platform-config.d.ts +41 -1
  175. package/dist/features/harness/platform-config.js +106 -9
  176. package/dist/features/harness/route-guess.d.ts +13 -0
  177. package/dist/features/harness/route-guess.js +49 -0
  178. package/dist/features/harness/script/app-controls.d.ts +5 -0
  179. package/dist/features/harness/script/app-controls.js +40 -0
  180. package/dist/features/harness/script/lane-ids.d.ts +14 -0
  181. package/dist/features/harness/script/lane-ids.js +16 -0
  182. package/dist/features/harness/script/script-run.d.ts +70 -0
  183. package/dist/features/harness/script/script-run.js +606 -0
  184. package/dist/features/license/license.d.ts +2 -2
  185. package/dist/features/visual/step-shots.d.ts +31 -0
  186. package/dist/features/visual/step-shots.js +231 -0
  187. package/dist/features/visual/visual-tools.d.ts +10 -1
  188. package/dist/features/visual/visual-tools.js +62 -17
  189. package/dist/hooks/client-directory.d.ts +18 -0
  190. package/dist/hooks/client-directory.js +57 -0
  191. package/dist/hooks/coding-agents.d.ts +34 -0
  192. package/dist/hooks/coding-agents.js +117 -0
  193. package/dist/hooks/driven-by.d.ts +23 -0
  194. package/dist/hooks/driven-by.js +56 -0
  195. package/dist/hooks/hook-bus.d.ts +4 -0
  196. package/dist/hooks/hook-bus.js +18 -0
  197. package/dist/hooks/hook-commands.js +15 -4
  198. package/dist/hooks/tool-log.d.ts +9 -0
  199. package/dist/hooks/tool-log.js +29 -0
  200. package/dist/index.d.ts +2 -0
  201. package/dist/index.js +80 -243
  202. package/dist/judgement/capsule/capsule-store.d.ts +2 -2
  203. package/dist/judgement/capsule/causal-summary.d.ts +15 -1
  204. package/dist/judgement/capsule/causal-summary.js +33 -0
  205. package/dist/judgement/runs/artifact/build-verification-run.d.ts +1 -1
  206. package/dist/judgement/runs/artifact/build-verification-run.js +25 -3
  207. package/dist/judgement/runs/artifact/gap-summary.d.ts +0 -18
  208. package/dist/judgement/runs/artifact/gap-summary.js +12 -3
  209. package/dist/judgement/runs/artifact/replay-mapping.d.ts +1 -1
  210. package/dist/judgement/runs/artifact/replay-mapping.js +37 -11
  211. package/dist/judgement/runs/artifact/run-store.d.ts +14 -2
  212. package/dist/judgement/runs/artifact/run-store.js +195 -3
  213. package/dist/judgement/runs/context-tools.js +59 -1
  214. package/dist/judgement/runs/default-run-id.d.ts +3 -0
  215. package/dist/judgement/runs/default-run-id.js +16 -0
  216. package/dist/judgement/runs/drive-run.d.ts +71 -1
  217. package/dist/judgement/runs/drive-run.js +317 -5
  218. package/dist/judgement/runs/repair-prompt.js +1 -1
  219. package/dist/judgement/runs/resume.d.ts +44 -0
  220. package/dist/judgement/runs/resume.js +137 -0
  221. package/dist/judgement/runs/reticle-runner.js +13 -3
  222. package/dist/judgement/runs/runner-port.d.ts +5 -1
  223. package/dist/judgement/runs/runner-port.js +8 -2
  224. package/dist/judgement/runs/verification-sync.d.ts +5 -2
  225. package/dist/judgement/runs/verification-sync.js +40 -18
  226. package/dist/language/flows/change/gate.d.ts +10 -0
  227. package/dist/language/flows/change/gate.js +22 -0
  228. package/dist/language/flows/decision.js +36 -1
  229. package/dist/language/flows/fields/flow-secret-field.d.ts +12 -0
  230. package/dist/language/flows/fields/flow-secret-field.js +25 -2
  231. package/dist/language/flows/flow-anchor.d.ts +61 -10
  232. package/dist/language/flows/flow-anchor.js +98 -6
  233. package/dist/language/flows/flow-cross-step.d.ts +15 -0
  234. package/dist/language/flows/flow-cross-step.js +21 -0
  235. package/dist/language/flows/flow-intent.d.ts +1 -0
  236. package/dist/language/flows/flow-journey.d.ts +6 -0
  237. package/dist/language/flows/flow-journey.js +20 -0
  238. package/dist/language/flows/flow-learning.d.ts +4 -0
  239. package/dist/language/flows/flow-learning.js +45 -14
  240. package/dist/language/flows/flow-mutate-tools.js +5 -3
  241. package/dist/language/flows/flow-replay-run.d.ts +23 -9
  242. package/dist/language/flows/flow-replay-run.js +115 -110
  243. package/dist/language/flows/flow-replay-types.d.ts +1 -1
  244. package/dist/language/flows/flow-replay.d.ts +40 -1
  245. package/dist/language/flows/flow-replay.js +110 -22
  246. package/dist/language/flows/flow-result.d.ts +22 -1
  247. package/dist/language/flows/flow-result.js +47 -2
  248. package/dist/language/flows/flow-scope.js +5 -1
  249. package/dist/language/flows/flow-step-runners.d.ts +22 -2
  250. package/dist/language/flows/flow-step-runners.js +247 -47
  251. package/dist/language/flows/flow-store-for-session.d.ts +2 -2
  252. package/dist/language/flows/flow-store-for-session.js +7 -3
  253. package/dist/language/flows/flow-tools.d.ts +10 -0
  254. package/dist/language/flows/flow-tools.js +112 -89
  255. package/dist/language/flows/flows.d.ts +16 -1
  256. package/dist/language/flows/flows.js +74 -12
  257. package/dist/language/flows/heal-run.js +6 -4
  258. package/dist/language/flows/narrow-write.d.ts +7 -0
  259. package/dist/language/flows/narrow-write.js +36 -1
  260. package/dist/language/flows/recording/flake.d.ts +2 -2
  261. package/dist/language/flows/recording/recording-backtrack.js +14 -1
  262. package/dist/language/flows/recording/tape/recordings.d.ts +58 -4
  263. package/dist/language/flows/recording/tape/recordings.js +95 -13
  264. package/dist/language/flows/replay-from-hud.d.ts +20 -0
  265. package/dist/language/flows/replay-from-hud.js +51 -0
  266. package/dist/language/flows/replay.d.ts +9 -5
  267. package/dist/language/flows/replay.js +56 -15
  268. package/dist/language/flows/single-replay.d.ts +7 -0
  269. package/dist/language/flows/single-replay.js +26 -0
  270. package/dist/language/flows/suite/suite-flakes.js +2 -0
  271. package/dist/language/flows/suite-selection.d.ts +2 -15
  272. package/dist/language/flows/suite-selection.js +4 -3
  273. package/dist/language/flows/verify-change-tools.d.ts +1 -0
  274. package/dist/language/flows/verify-change-tools.js +202 -191
  275. package/dist/launch-chromium.d.ts +27 -5
  276. package/dist/launch-chromium.js +38 -8
  277. package/dist/memory/cloud/cloud-config.d.ts +22 -1
  278. package/dist/memory/cloud/cloud-config.js +34 -5
  279. package/dist/memory/cloud/cloud-sync.d.ts +17 -14
  280. package/dist/memory/cloud/cloud-sync.js +15 -4
  281. package/dist/memory/cloud/harness-config.d.ts +75 -1
  282. package/dist/memory/cloud/harness-config.js +115 -24
  283. package/dist/memory/cloud/hud-notices-source.d.ts +21 -0
  284. package/dist/memory/cloud/hud-notices-source.js +128 -0
  285. package/dist/memory/cloud/memory-scope.d.ts +161 -0
  286. package/dist/memory/cloud/memory-scope.js +227 -0
  287. package/dist/memory/cloud/sync-cycle.d.ts +44 -7
  288. package/dist/memory/cloud/sync-cycle.js +129 -15
  289. package/dist/memory/cloud/sync-daemon.d.ts +11 -0
  290. package/dist/memory/cloud/sync-daemon.js +121 -9
  291. package/dist/memory/cloud/sync-disk.d.ts +1 -1
  292. package/dist/memory/cloud/sync-disk.js +31 -10
  293. package/dist/memory/cloud/sync-screenshots.d.ts +32 -0
  294. package/dist/memory/cloud/sync-screenshots.js +105 -0
  295. package/dist/memory/cloud/unsynced-roots.d.ts +47 -0
  296. package/dist/memory/cloud/unsynced-roots.js +166 -0
  297. package/dist/memory/impact/account-watch.d.ts +34 -0
  298. package/dist/memory/impact/account-watch.js +59 -0
  299. package/dist/memory/impact/impact-recorder.d.ts +8 -0
  300. package/dist/memory/impact/impact-recorder.js +12 -2
  301. package/dist/memory/impact/impact-store.d.ts +17 -4
  302. package/dist/memory/impact/impact-store.js +169 -63
  303. package/dist/memory/intent/inline-intent.d.ts +1 -0
  304. package/dist/memory/intent/intent-shard-store.js +16 -4
  305. package/dist/memory/intent/intent-shard.d.ts +14 -2
  306. package/dist/memory/intent/intent-store.d.ts +1 -0
  307. package/dist/memory/intent/intent-tools.js +122 -4
  308. package/dist/memory/intent/open-intents.d.ts +6 -0
  309. package/dist/memory/intent/open-intents.js +10 -1
  310. package/dist/memory/intent/settled-words.d.ts +24 -0
  311. package/dist/memory/intent/settled-words.js +130 -0
  312. package/dist/memory/journal/drive-flow.d.ts +16 -0
  313. package/dist/memory/journal/drive-flow.js +34 -2
  314. package/dist/memory/journal/drive-run-flush.d.ts +2 -0
  315. package/dist/memory/journal/drive-run-flush.js +20 -5
  316. package/dist/memory/journal/envelope.d.ts +8 -8
  317. package/dist/memory/journal/journal-recorder.js +52 -3
  318. package/dist/memory/journal/on-disk/retention.d.ts +8 -0
  319. package/dist/memory/journal/on-disk/retention.js +32 -2
  320. package/dist/memory/journal/on-disk/startup-maintenance.js +3 -1
  321. package/dist/memory/journal/on-disk/workspace-tiers.js +9 -4
  322. package/dist/memory/journal/session-end.d.ts +15 -1
  323. package/dist/memory/journal/session-end.js +42 -9
  324. package/dist/memory/project/artifact-root-resolver.d.ts +26 -1
  325. package/dist/memory/project/artifact-root-resolver.js +125 -16
  326. package/dist/memory/project/artifact-root.d.ts +46 -8
  327. package/dist/memory/project/artifact-root.js +89 -10
  328. package/dist/memory/project/dir/reticle-dir.d.ts +18 -2
  329. package/dist/memory/project/dir/reticle-dir.js +26 -3
  330. package/dist/memory/project/link-directory.d.ts +15 -0
  331. package/dist/memory/project/link-directory.js +44 -0
  332. package/dist/memory/project/project-tools.js +22 -3
  333. package/dist/memory/project/root-claims.d.ts +6 -0
  334. package/dist/memory/project/root-claims.js +39 -0
  335. package/dist/memory/project/session-root.d.ts +26 -1
  336. package/dist/memory/project/session-root.js +49 -5
  337. package/dist/memory/project/sync-status.d.ts +85 -0
  338. package/dist/memory/project/sync-status.js +224 -0
  339. package/dist/memory/recall/memory-tools.js +47 -2
  340. package/dist/memory/recall/platform-memory.d.ts +34 -0
  341. package/dist/memory/recall/platform-memory.js +139 -0
  342. package/dist/memory/recall/project-memory.d.ts +19 -0
  343. package/dist/memory/recall/project-memory.js +80 -13
  344. package/dist/portal/bridge/bridge.d.ts +9 -3
  345. package/dist/portal/bridge/bridge.js +82 -22
  346. package/dist/portal/bridge/harness-request.d.ts +23 -0
  347. package/dist/portal/bridge/harness-request.js +27 -0
  348. package/dist/portal/bridge/pairing-token.d.ts +9 -0
  349. package/dist/portal/bridge/pairing-token.js +17 -0
  350. package/dist/portal/bridge/wrong-path.d.ts +17 -0
  351. package/dist/portal/bridge/wrong-path.js +35 -0
  352. package/dist/portal/input/network-mock-tools.js +11 -2
  353. package/dist/portal/input/real-input.d.ts +83 -10
  354. package/dist/portal/input/real-input.js +218 -30
  355. package/dist/portal/input/viewport-tools.js +3 -3
  356. package/dist/portal/pool/browser-pool.d.ts +70 -9
  357. package/dist/portal/pool/browser-pool.js +190 -37
  358. package/dist/portal/pool/context-permissions.d.ts +33 -0
  359. package/dist/portal/pool/context-permissions.js +33 -0
  360. package/dist/portal/pool/lease-marker-stamp.d.ts +23 -0
  361. package/dist/portal/pool/lease-marker-stamp.js +49 -0
  362. package/dist/portal/pool/playwright-launcher.d.ts +35 -0
  363. package/dist/portal/pool/playwright-launcher.js +64 -3
  364. package/dist/portal/pool/pool-contract.d.ts +28 -5
  365. package/dist/portal/session/agent-link.d.ts +8 -0
  366. package/dist/portal/session/agent-link.js +17 -0
  367. package/dist/portal/session/command-payload.d.ts +8 -0
  368. package/dist/portal/session/command-payload.js +24 -0
  369. package/dist/portal/session/daemon-answers.d.ts +3 -3
  370. package/dist/portal/session/daemon-answers.js +4 -2
  371. package/dist/portal/session/dev-server/dev-command.d.ts +21 -0
  372. package/dist/portal/session/dev-server/dev-command.js +87 -12
  373. package/dist/portal/session/facts/departure-note.d.ts +22 -0
  374. package/dist/portal/session/facts/departure-note.js +47 -0
  375. package/dist/portal/session/facts/observed-state.d.ts +16 -18
  376. package/dist/portal/session/facts/observed-state.js +46 -3
  377. package/dist/portal/session/facts/session-replaced.d.ts +5 -0
  378. package/dist/portal/session/facts/session-replaced.js +5 -0
  379. package/dist/portal/session/first-run-wiring.d.ts +42 -0
  380. package/dist/portal/session/first-run-wiring.js +132 -0
  381. package/dist/portal/session/human/live-control.d.ts +9 -2
  382. package/dist/portal/session/human/live-control.js +12 -3
  383. package/dist/portal/session/human/notes-ledger.d.ts +20 -0
  384. package/dist/portal/session/human/notes-ledger.js +55 -0
  385. package/dist/portal/session/human/review-store.d.ts +12 -0
  386. package/dist/portal/session/human/review-store.js +18 -10
  387. package/dist/portal/session/live-control-tools.d.ts +0 -13
  388. package/dist/portal/session/live-control-tools.js +107 -13
  389. package/dist/portal/session/narration-args.d.ts +5 -0
  390. package/dist/portal/session/narration-args.js +17 -0
  391. package/dist/portal/session/no-session-diagnosis.d.ts +8 -1
  392. package/dist/portal/session/no-session-diagnosis.js +46 -9
  393. package/dist/portal/session/no-session-next-action.d.ts +31 -0
  394. package/dist/portal/session/no-session-next-action.js +159 -60
  395. package/dist/portal/session/no-session-watch.d.ts +14 -1
  396. package/dist/portal/session/no-session-watch.js +227 -17
  397. package/dist/portal/session/presence/session-lease.js +1 -1
  398. package/dist/portal/session/presence/session-mirror.js +3 -0
  399. package/dist/portal/session/recorded-gaps.d.ts +97 -0
  400. package/dist/portal/session/recorded-gaps.js +251 -0
  401. package/dist/portal/session/serving-directory.d.ts +27 -0
  402. package/dist/portal/session/serving-directory.js +45 -0
  403. package/dist/portal/session/session-health.d.ts +18 -0
  404. package/dist/portal/session/session-health.js +9 -1
  405. package/dist/portal/session/session-info.d.ts +8 -1
  406. package/dist/portal/session/session-info.js +8 -1
  407. package/dist/portal/session/session-manager.d.ts +39 -0
  408. package/dist/portal/session/session-manager.js +98 -11
  409. package/dist/portal/session/session-reaper.js +4 -1
  410. package/dist/portal/session/session-successor.d.ts +13 -0
  411. package/dist/portal/session/session-successor.js +1 -1
  412. package/dist/portal/session/session-tools.js +26 -3
  413. package/dist/portal/session/session.d.ts +22 -30
  414. package/dist/portal/session/session.js +81 -79
  415. package/dist/portal/session/timing/command-timeout.js +25 -1
  416. package/dist/portal/session/timing/wall-clock.d.ts +1 -1
  417. package/dist/portal/session/timing/wall-clock.js +1 -1
  418. package/dist/portal/session/zero-install-first-call.d.ts +31 -0
  419. package/dist/portal/session/zero-install-first-call.js +88 -0
  420. package/dist/start-options.d.ts +12 -0
  421. package/dist/start-wiring.d.ts +63 -0
  422. package/dist/start-wiring.js +169 -0
  423. package/dist/status-payload.d.ts +23 -1
  424. package/dist/status-payload.js +36 -1
  425. package/dist/surface/http-server.js +18 -2
  426. package/dist/surface/mcp/attach-memory.d.ts +1 -0
  427. package/dist/surface/mcp/attach-memory.js +12 -1
  428. package/dist/surface/mcp/mcp-proxy.d.ts +9 -0
  429. package/dist/surface/mcp/mcp-proxy.js +65 -13
  430. package/dist/surface/mcp/mcp.d.ts +34 -6
  431. package/dist/surface/mcp/mcp.js +61 -17
  432. package/dist/surface/mcp/proxy/drive-calls.d.ts +10 -0
  433. package/dist/surface/mcp/proxy/drive-calls.js +27 -0
  434. package/dist/surface/mcp/proxy/mcp-post-transport.js +5 -0
  435. package/dist/surface/mcp/server-instructions.d.ts +4 -2
  436. package/dist/surface/mcp/server-instructions.js +32 -14
  437. package/dist/surface/mcp/shared-params.js +2 -2
  438. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  439. package/dist/surface/mcp/surface-vocabulary.js +1 -0
  440. package/dist/surface/tools/account-tool.d.ts +22 -0
  441. package/dist/surface/tools/account-tool.js +157 -0
  442. package/dist/surface/tools/act/act-danger.d.ts +11 -0
  443. package/dist/surface/tools/act/act-danger.js +38 -1
  444. package/dist/surface/tools/act/act-observation.d.ts +33 -0
  445. package/dist/surface/tools/act/act-observation.js +57 -0
  446. package/dist/surface/tools/act/act-preflight.js +25 -2
  447. package/dist/surface/tools/act/act-target.js +8 -3
  448. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +8 -0
  449. package/dist/surface/tools/act/navigation/navigate-arrival.js +56 -2
  450. package/dist/surface/tools/act/resolve-target.d.ts +1 -1
  451. package/dist/surface/tools/act/resolve-target.js +18 -3
  452. package/dist/surface/tools/act/screen-request.d.ts +34 -0
  453. package/dist/surface/tools/act/screen-request.js +113 -0
  454. package/dist/surface/tools/act/sequence-grade.d.ts +8 -0
  455. package/dist/surface/tools/act/sequence-grade.js +13 -2
  456. package/dist/surface/tools/act/settle-in-flight.js +25 -3
  457. package/dist/surface/tools/act-sequence-tool.js +138 -29
  458. package/dist/surface/tools/act-tools.d.ts +0 -9
  459. package/dist/surface/tools/act-tools.js +71 -41
  460. package/dist/surface/tools/agent/app-notes-learning.d.ts +7 -0
  461. package/dist/surface/tools/agent/app-notes-learning.js +80 -0
  462. package/dist/surface/tools/agent/app-notes.d.ts +4 -0
  463. package/dist/surface/tools/agent/app-notes.js +48 -0
  464. package/dist/surface/tools/agent/build-staleness.d.ts +7 -0
  465. package/dist/surface/tools/agent/build-staleness.js +62 -0
  466. package/dist/surface/tools/agent/compact-envelope.d.ts +80 -0
  467. package/dist/surface/tools/agent/compact-envelope.js +374 -0
  468. package/dist/surface/tools/agent/for-the-agent.d.ts +12 -0
  469. package/dist/surface/tools/agent/for-the-agent.js +44 -0
  470. package/dist/surface/tools/agent/notices.d.ts +50 -0
  471. package/dist/surface/tools/agent/notices.js +152 -0
  472. package/dist/surface/tools/agent/reteach.d.ts +11 -0
  473. package/dist/surface/tools/agent/reteach.js +50 -0
  474. package/dist/surface/tools/args/alias-args.d.ts +5 -0
  475. package/dist/surface/tools/args/alias-args.js +10 -0
  476. package/dist/surface/tools/args/confirm-dangerous-arg.d.ts +6 -0
  477. package/dist/surface/tools/args/confirm-dangerous-arg.js +6 -0
  478. package/dist/surface/tools/assert/assert-source.d.ts +15 -0
  479. package/dist/surface/tools/assert/assert-source.js +40 -0
  480. package/dist/surface/tools/assert/assert-verdict.d.ts +1 -1
  481. package/dist/surface/tools/assert/assert-verdict.js +42 -17
  482. package/dist/surface/tools/browser-tools.js +51 -2
  483. package/dist/surface/tools/chat-drives.d.ts +101 -0
  484. package/dist/surface/tools/chat-drives.js +409 -0
  485. package/dist/surface/tools/coverage-tools.d.ts +2 -10
  486. package/dist/surface/tools/coverage-tools.js +2 -53
  487. package/dist/surface/tools/dynamic-tools.js +29 -1
  488. package/dist/surface/tools/empty-session-list.d.ts +51 -0
  489. package/dist/surface/tools/empty-session-list.js +120 -0
  490. package/dist/surface/tools/error-recovery.d.ts +14 -0
  491. package/dist/surface/tools/error-recovery.js +105 -8
  492. package/dist/surface/tools/explore-tools.d.ts +31 -1
  493. package/dist/surface/tools/explore-tools.js +438 -66
  494. package/dist/surface/tools/feedback-tools.d.ts +2 -3
  495. package/dist/surface/tools/feedback-tools.js +18 -3
  496. package/dist/surface/tools/gap-novelty.d.ts +3 -1
  497. package/dist/surface/tools/gap-novelty.js +8 -1
  498. package/dist/surface/tools/harness-explore.d.ts +118 -57
  499. package/dist/surface/tools/harness-explore.js +344 -292
  500. package/dist/surface/tools/harness-gate.d.ts +88 -0
  501. package/dist/surface/tools/harness-gate.js +235 -0
  502. package/dist/surface/tools/harness-hunt.d.ts +43 -0
  503. package/dist/surface/tools/harness-hunt.js +215 -0
  504. package/dist/surface/tools/harness-plan.d.ts +17 -0
  505. package/dist/surface/tools/harness-plan.js +38 -0
  506. package/dist/surface/tools/harness-script.d.ts +30 -0
  507. package/dist/surface/tools/harness-script.js +560 -0
  508. package/dist/surface/tools/harness-tab-guard.d.ts +5 -0
  509. package/dist/surface/tools/harness-tab-guard.js +27 -0
  510. package/dist/surface/tools/harness-toolset.d.ts +22 -1
  511. package/dist/surface/tools/harness-toolset.js +101 -7
  512. package/dist/surface/tools/human-notes.d.ts +16 -0
  513. package/dist/surface/tools/human-notes.js +27 -0
  514. package/dist/surface/tools/invoke-tool.d.ts +5 -2
  515. package/dist/surface/tools/invoke-tool.js +263 -85
  516. package/dist/surface/tools/lease-hint.js +26 -5
  517. package/dist/surface/tools/lease-permissions.d.ts +33 -0
  518. package/dist/surface/tools/lease-permissions.js +73 -0
  519. package/dist/surface/tools/lease-readiness.d.ts +38 -0
  520. package/dist/surface/tools/lease-readiness.js +57 -0
  521. package/dist/surface/tools/lease-seed.d.ts +49 -0
  522. package/dist/surface/tools/lease-seed.js +167 -0
  523. package/dist/surface/tools/lease-tools.d.ts +13 -24
  524. package/dist/surface/tools/lease-tools.js +146 -197
  525. package/dist/surface/tools/live-call-text.js +4 -4
  526. package/dist/surface/tools/merge-tools.js +42 -2
  527. package/dist/surface/tools/merged-name-redirect.js +4 -0
  528. package/dist/surface/tools/next-step.d.ts +29 -0
  529. package/dist/surface/tools/next-step.js +214 -0
  530. package/dist/surface/tools/observe-tools.js +45 -9
  531. package/dist/surface/tools/platform-moment.d.ts +72 -0
  532. package/dist/surface/tools/platform-moment.js +296 -0
  533. package/dist/surface/tools/query-hint-schema.d.ts +7 -0
  534. package/dist/surface/tools/query-hint-schema.js +10 -0
  535. package/dist/surface/tools/read-tools.js +71 -14
  536. package/dist/surface/tools/real-input-attempt.js +96 -13
  537. package/dist/surface/tools/reticle-result.d.ts +120 -0
  538. package/dist/surface/tools/reticle-result.js +364 -0
  539. package/dist/surface/tools/tool-kit.d.ts +83 -3
  540. package/dist/surface/tools/tool-kit.js +77 -7
  541. package/dist/surface/tools/tools.js +43 -59
  542. package/dist/telemetry/account-envelope.d.ts +7 -0
  543. package/dist/telemetry/account-envelope.js +61 -0
  544. package/dist/telemetry/agent-client.d.ts +15 -0
  545. package/dist/telemetry/agent-client.js +57 -0
  546. package/dist/telemetry/app-instrumented.js +9 -2
  547. package/dist/telemetry/argument-shape.d.ts +7 -0
  548. package/dist/telemetry/argument-shape.js +86 -1
  549. package/dist/telemetry/automation-hint.d.ts +2 -3
  550. package/dist/telemetry/automation-hint.js +33 -8
  551. package/dist/telemetry/cli-telemetry.js +7 -2
  552. package/dist/telemetry/daemon-telemetry.js +3 -0
  553. package/dist/telemetry/error-fingerprint.d.ts +10 -1
  554. package/dist/telemetry/error-fingerprint.js +61 -5
  555. package/dist/telemetry/feedback-cli.d.ts +0 -13
  556. package/dist/telemetry/feedback-cli.js +0 -77
  557. package/dist/telemetry/feedback.d.ts +13 -0
  558. package/dist/telemetry/feedback.js +183 -17
  559. package/dist/telemetry/harness-run.d.ts +18 -0
  560. package/dist/telemetry/harness-run.js +80 -0
  561. package/dist/telemetry/hud-events.d.ts +16 -0
  562. package/dist/telemetry/hud-events.js +27 -0
  563. package/dist/telemetry/hud-metrics.d.ts +29 -6
  564. package/dist/telemetry/hud-metrics.js +116 -25
  565. package/dist/telemetry/init-telemetry.js +2 -2
  566. package/dist/telemetry/install-source.d.ts +13 -25
  567. package/dist/telemetry/install-source.js +44 -3
  568. package/dist/telemetry/mcp-connection.d.ts +0 -14
  569. package/dist/telemetry/mcp-connection.js +5 -1
  570. package/dist/telemetry/onboarding-firsts.d.ts +32 -0
  571. package/dist/telemetry/onboarding-firsts.js +81 -6
  572. package/dist/telemetry/platform-linked.d.ts +5 -0
  573. package/dist/telemetry/platform-linked.js +32 -0
  574. package/dist/telemetry/project-profile.js +5 -3
  575. package/dist/telemetry/proxy-telemetry.js +4 -1
  576. package/dist/telemetry/session-metrics.d.ts +15 -4
  577. package/dist/telemetry/session-metrics.js +88 -52
  578. package/dist/telemetry/telemetry.d.ts +38 -2
  579. package/dist/telemetry/telemetry.js +144 -34
  580. package/dist/telemetry/verification-of.d.ts +3 -1
  581. package/dist/telemetry/verification-of.js +13 -4
  582. package/dist/wire-journal.d.ts +2 -2
  583. package/dist/wire-journal.js +20 -6
  584. package/docs/README.md +3 -3
  585. package/docs/actions.mdx +6 -3
  586. package/docs/adapters.md +1 -1
  587. package/docs/agent-cheatsheet.md +25 -28
  588. package/docs/architecture.md +5 -5
  589. package/docs/autodrive.md +33 -28
  590. package/docs/benchmarks.md +5 -5
  591. package/docs/best-practices.mdx +27 -9
  592. package/docs/capabilities.mdx +14 -16
  593. package/docs/cli/affected.mdx +1 -1
  594. package/docs/cli/cloud.mdx +54 -75
  595. package/docs/cli/doctor.mdx +14 -3
  596. package/docs/cli/drive.mdx +20 -14
  597. package/docs/cli/feedback.mdx +3 -3
  598. package/docs/cli/gate.mdx +7 -2
  599. package/docs/cli/help.mdx +50 -55
  600. package/docs/cli/init.mdx +43 -28
  601. package/docs/cli/license.mdx +12 -8
  602. package/docs/cli/mcp.mdx +4 -4
  603. package/docs/cli/open.mdx +3 -3
  604. package/docs/cli/report.mdx +4 -2
  605. package/docs/cli/restart.mdx +2 -2
  606. package/docs/cli/rollback.mdx +14 -8
  607. package/docs/cli/serve.mdx +4 -4
  608. package/docs/cli/setup.mdx +23 -0
  609. package/docs/cli/status.mdx +18 -5
  610. package/docs/cli/stop.mdx +67 -8
  611. package/docs/cli/try.mdx +62 -0
  612. package/docs/cli/update.mdx +4 -3
  613. package/docs/cli/verify.mdx +17 -6
  614. package/docs/cli/version.mdx +5 -4
  615. package/docs/cli.mdx +111 -42
  616. package/docs/debugging.md +3 -3
  617. package/docs/deploy-checks.md +1 -1
  618. package/docs/desktop.mdx +9 -3
  619. package/docs/enterprise.md +11 -10
  620. package/docs/environment.mdx +7 -8
  621. package/docs/faq.mdx +3 -3
  622. package/docs/first-drive.md +5 -4
  623. package/docs/flows.md +35 -27
  624. package/docs/for-agents.md +4 -5
  625. package/docs/frameworks.mdx +32 -21
  626. package/docs/getting-started.md +65 -61
  627. package/docs/human-control.md +16 -16
  628. package/docs/index.mdx +15 -42
  629. package/docs/install-agentic.mdx +22 -37
  630. package/docs/install-manual.mdx +23 -13
  631. package/docs/instrumentation.mdx +5 -3
  632. package/docs/integration-patterns.md +7 -7
  633. package/docs/license-activation.mdx +1 -1
  634. package/docs/multi-agent-testing.md +24 -6
  635. package/docs/packages/babel-plugin.mdx +4 -2
  636. package/docs/packages/browser.mdx +16 -10
  637. package/docs/packages/core.mdx +12 -5
  638. package/docs/packages/electron.mdx +3 -2
  639. package/docs/packages/engine.mdx +1 -1
  640. package/docs/packages/eslint-plugin.mdx +18 -12
  641. package/docs/packages/next.mdx +8 -7
  642. package/docs/packages/open-verification.mdx +1 -1
  643. package/docs/packages/server.mdx +5 -5
  644. package/docs/packages/tauri.mdx +9 -9
  645. package/docs/packages/test.mdx +4 -10
  646. package/docs/packages/vite-plugin.mdx +32 -4
  647. package/docs/packages.mdx +11 -15
  648. package/docs/platform-integration.md +6 -5
  649. package/docs/predicates.mdx +40 -24
  650. package/docs/quickstart.mdx +69 -30
  651. package/docs/required-checks.md +8 -7
  652. package/docs/reticle-manual.md +15 -5
  653. package/docs/skill-file.mdx +17 -79
  654. package/docs/skill-setup.md +9 -9
  655. package/docs/skill.md +200 -0
  656. package/docs/system-map.md +7 -7
  657. package/docs/telemetry.md +23 -25
  658. package/docs/testing.md +4 -4
  659. package/docs/token-efficiency.md +12 -12
  660. package/docs/tools/act-and-wait.mdx +9 -2
  661. package/docs/tools/act-sequence.mdx +6 -7
  662. package/docs/tools/act.mdx +2 -2
  663. package/docs/tools/assert.mdx +18 -17
  664. package/docs/tools/console.mdx +9 -9
  665. package/docs/tools/inspect.mdx +16 -7
  666. package/docs/tools/look.mdx +24 -0
  667. package/docs/tools/navigate.mdx +5 -3
  668. package/docs/tools/network.mdx +2 -2
  669. package/docs/tools/observe.mdx +6 -6
  670. package/docs/tools/overview.mdx +34 -33
  671. package/docs/tools/query.mdx +5 -5
  672. package/docs/tools/session-and-feedback.mdx +22 -13
  673. package/docs/tools/sessions.mdx +8 -8
  674. package/docs/tools/snapshot.mdx +6 -5
  675. package/docs/tools/state.mdx +9 -9
  676. package/docs/tools/tools-and-run.mdx +26 -24
  677. package/docs/tools/verify.mdx +28 -0
  678. package/docs/tools/wait-for.mdx +8 -4
  679. package/docs/troubleshooting.mdx +21 -6
  680. package/docs/usage.md +55 -46
  681. package/docs/use-cases.mdx +3 -3
  682. package/docs/vs-chrome-devtools-mcp.mdx +9 -6
  683. package/docs/vs-playwright-mcp.mdx +3 -1
  684. package/docs/what-is-recorded.md +17 -5
  685. package/docs/what-is-reticle.mdx +5 -3
  686. package/docs/why-reticle.mdx +1 -1
  687. package/package.json +9 -12
  688. package/dist/command/cli/demo/demo-page.d.ts +0 -78
  689. package/dist/command/cli/demo/demo-page.js +0 -241
  690. package/dist/command/cli/demo/demo-run.d.ts +0 -38
  691. package/dist/command/cli/demo/demo-run.js +0 -138
  692. package/dist/features/harness/driver.d.ts +0 -50
  693. package/dist/features/harness/driver.js +0 -236
  694. package/dist/features/harness/jev-driver.d.ts +0 -188
  695. package/dist/features/harness/jev-driver.js +0 -678
  696. package/dist/features/harness/openai-driver.d.ts +0 -58
  697. package/dist/features/harness/openai-driver.js +0 -206
  698. package/dist/judgement/hunt/hunt-report.d.ts +0 -49
  699. package/dist/judgement/hunt/hunt-report.js +0 -84
  700. package/dist/language/flows/buddy-status.d.ts +0 -25
  701. package/dist/language/flows/buddy-status.js +0 -32
  702. package/dist/language/flows/change/watch-batcher.d.ts +0 -18
  703. package/dist/language/flows/change/watch-batcher.js +0 -19
  704. package/dist/memory/cloud/harness-offer.d.ts +0 -44
  705. package/dist/memory/cloud/harness-offer.js +0 -145
  706. package/dist/memory/project/dir/fill-value-store.d.ts +0 -32
  707. package/dist/memory/project/dir/fill-value-store.js +0 -88
  708. package/dist/surface/tools/feature-capture.d.ts +0 -151
  709. package/dist/surface/tools/feature-capture.js +0 -235
  710. package/dist/surface/tools/tool-hit-rate.d.ts +0 -87
  711. package/dist/surface/tools/tool-hit-rate.js +0 -90
  712. package/dist/telemetry/harness-drive.d.ts +0 -4
  713. package/dist/telemetry/harness-drive.js +0 -35
  714. package/dist/telemetry/identify.d.ts +0 -22
  715. package/dist/telemetry/identify.js +0 -73
  716. package/docs/cli/capsules.mdx +0 -70
  717. package/docs/cli/hunt.mdx +0 -73
  718. package/docs/cli/identify.mdx +0 -76
  719. package/docs/cli/kill.mdx +0 -91
  720. package/docs/cli/watch.mdx +0 -62
  721. package/docs/local-registry.md +0 -143
@@ -62,7 +62,8 @@ interface SessionEndDeps {
62
62
  * directories into `language/flows/recording/tape/` for a constant and two types was a dependency
63
63
  * nobody needed. `directory-reach.test.ts` refused it, and its advice was right.
64
64
  */
65
- takeAmbientTape?: () => {
65
+ /** The tape of THIS tab's steps: another app's drive is never saved into this project. */
66
+ takeAmbientTape?: (sessionId: string) => {
66
67
  steps: readonly TapeStep[];
67
68
  startPath?: string;
68
69
  } | undefined;
@@ -120,4 +121,17 @@ export declare function makeSessionEnd(deps: SessionEndDeps): (session: SessionE
120
121
  * dashboard.
121
122
  */
122
123
  export declare function recordDriveRun(deps: SessionEndDeps, session: SessionEndTarget): Promise<void>;
124
+ /**
125
+ * Tear down every session still open, and wait for it.
126
+ *
127
+ * Teardown is what saves a tab's driven flow and its last run, and it normally fires from the
128
+ * socket's close handler, unawaited. A daemon closing with tabs open terminated those sockets after
129
+ * its final sync, so the flow was written too late to be sent, or not at all once the process
130
+ * exited. Called before that final sync instead: each session leaves the registry first, so the
131
+ * socket closing later finds nothing to tear down a second time.
132
+ */
133
+ export declare function endLiveSessions<S>(sessions: {
134
+ all: () => S[];
135
+ remove: (session: S) => boolean;
136
+ }, teardown: (session: S) => Promise<void>): Promise<void>;
123
137
  export {};
@@ -1,4 +1,5 @@
1
- import { DRIVE_FLOW_PREFIX, driveFlowsFrom, } from './drive-flow.js';
1
+ import { DRIVE_FLOW_PREFIX, DriveFlowSkipReason, driveFlowsFrom, } from './drive-flow.js';
2
+ import { basename, dirname } from 'node:path';
2
3
  import { log } from '../../log.js';
3
4
  /** What a session's teardown writes to the daemon log when a by-product fails to save. */
4
5
  const SESSION_END_LOG = {
@@ -7,13 +8,13 @@ const SESSION_END_LOG = {
7
8
  };
8
9
  const errorMessage = (error) => error instanceof Error ? error.message : String(error);
9
10
  import { journalActionsPath, sessionDirPath } from '../project/dir/reticle-dir.js';
10
- import { asSessionId } from '@reticlehq/core';
11
+ import { asRunId, asSessionId } from '@reticlehq/core';
11
12
  import { OnboardingPhase, OnboardingStepStatus, } from '@reticlehq/core/telemetry';
12
13
  import { AmbientStore } from './ambient-store.js';
13
14
  import { subjectOf } from '@reticlehq/core';
14
15
  import { pruneWorkspace } from './on-disk/startup-maintenance.js';
15
16
  import { buildVerificationRun } from '../../judgement/runs/artifact/build-verification-run.js';
16
- import { driveRunFrom, driveRunId } from '../../judgement/runs/drive-run.js';
17
+ import { agentEndClaims, agentEndCounts, driveRunId, driveRunsFrom, } from '../../judgement/runs/drive-run.js';
17
18
  import { RunStore } from '../../judgement/runs/artifact/run-store.js';
18
19
  /**
19
20
  * Build the teardown handler the bridge fires when a session is removed. Flushes the journal first (so no
@@ -173,16 +174,21 @@ export async function recordDriveRun(deps, session) {
173
174
  if (session.readJournalActions === undefined)
174
175
  return;
175
176
  const actions = await session.readJournalActions();
176
- const input = driveRunFrom(actions, {
177
+ const root = session.artifactRoot ?? deps.reticleRoot;
178
+ const inputs = driveRunsFrom(actions, {
177
179
  // Derived from the session, NOT random. Teardown fires on every socket close, and a reconnecting
178
180
  // tab keeps its session id and appends to the same ledger — so a drive across two page reloads
179
181
  // would fold the whole ledger twice and publish two overlapping rows, the second a superset of
180
182
  // the first. A stable id makes the artifact idempotent: the same session rewrites its own run,
181
183
  // and the cloud diffs by runId, so a re-push supersedes rather than duplicates.
182
184
  runId: driveRunId(session.id),
185
+ // Each agent session in the tab, as `reticle_session {end}` cut them, is its own run.
186
+ agentEnds: agentEndCounts(session.id),
187
+ agentClaims: agentEndClaims(session.id),
183
188
  ...(session.projectId === undefined ? {} : { projectId: session.projectId }),
189
+ projectName: basename(dirname(root)),
184
190
  });
185
- if (input === undefined)
191
+ if (0 === inputs.length)
186
192
  return;
187
193
  // The protocol reaching the artifact a person actually reads. Only when the session could say
188
194
  // where it was: a subject with no locator is not a weaker subject, it is a guess, and the
@@ -196,12 +202,19 @@ export async function recordDriveRun(deps, session) {
196
202
  currentDocumentId: session.currentDocumentId,
197
203
  currentEditEpoch: session.currentEditEpoch,
198
204
  });
199
- const store = new RunStore(deps.fs, session.artifactRoot ?? deps.reticleRoot,
205
+ const store = new RunStore(deps.fs, root,
200
206
  // Tell the sync daemon a run landed, rather than letting it find out on its next tick. Teardown
201
207
  // used to write this artifact with no callback at all, so the evidence from a whole session sat
202
208
  // on disk until a timer noticed — which, for the last session of the day, could be never.
203
209
  deps.onRunPersisted === undefined ? undefined : { onWrote: deps.onRunPersisted });
204
- await store.write(buildVerificationRun({ ...input, ...(subject === undefined ? {} : { subject }) }, deps.now ?? (() => Date.now())));
210
+ const clock = deps.now ?? (() => Date.now());
211
+ for (const input of inputs) {
212
+ // A reload rewrites this session's run under the same id; the first stamp is when it ran, and
213
+ // restamping on every rewrite showed the tab's close as the drive's time.
214
+ const held = await store.read(asRunId(input.runId));
215
+ const firstAt = held.ok ? held.run.createdAt : undefined;
216
+ await store.write(buildVerificationRun({ ...input, ...(subject === undefined ? {} : { subject }) }, firstAt === undefined ? clock : () => firstAt));
217
+ }
205
218
  }
206
219
  /** Persist what this session drove as a replayable flow, when it declared anything provable. */
207
220
  async function saveDrivenFlow(deps, session) {
@@ -210,7 +223,7 @@ async function saveDrivenFlow(deps, session) {
210
223
  if (takeTape === undefined || flows === undefined)
211
224
  return;
212
225
  // One flow per journey the session contained, not one flow per session — see drive-flow.ts.
213
- const { programs, outcome } = driveFlowsFrom(takeTape());
226
+ const { programs, outcome } = driveFlowsFrom(takeTape(session.id));
214
227
  const author = deps.author?.();
215
228
  for (const program of programs) {
216
229
  // Folded into any saved copy of the same journey, so each session does not add a near-copy.
@@ -226,7 +239,13 @@ async function saveDrivenFlow(deps, session) {
226
239
  phase: OnboardingPhase.FIRST_RUN,
227
240
  step: 'flow_recorded',
228
241
  status: 0 === programs.length ? OnboardingStepStatus.SKIPPED : OnboardingStepStatus.COMPLETED,
229
- ...(outcome.unprovenSteps === undefined ? {} : { reason: 'no_declared_consequence' }),
242
+ ...(outcome.unprovenSteps === undefined
243
+ ? {}
244
+ : { reason: DriveFlowSkipReason.NO_DECLARED_CONSEQUENCE }),
245
+ // Not saved, with the reason: proved against mocked responses, which replay cannot see (#1459).
246
+ ...(0 < programs.length || outcome.mockedSteps === undefined
247
+ ? {}
248
+ : { reason: DriveFlowSkipReason.DRIVEN_UNDER_NETWORK_MOCKS }),
230
249
  });
231
250
  }
232
251
  /**
@@ -247,3 +266,17 @@ async function dropUndrivenJournal(fs, root, sessionId) {
247
266
  // tidying is never the reason a teardown fails
248
267
  }
249
268
  }
269
+ /**
270
+ * Tear down every session still open, and wait for it.
271
+ *
272
+ * Teardown is what saves a tab's driven flow and its last run, and it normally fires from the
273
+ * socket's close handler, unawaited. A daemon closing with tabs open terminated those sockets after
274
+ * its final sync, so the flow was written too late to be sent, or not at all once the process
275
+ * exited. Called before that final sync instead: each session leaves the registry first, so the
276
+ * socket closing later finds nothing to tear down a second time.
277
+ */
278
+ export async function endLiveSessions(sessions, teardown) {
279
+ const ending = sessions.all().filter((session) => sessions.remove(session));
280
+ // Teardown is best-effort everywhere it runs: one that fails must not stop the others.
281
+ await Promise.all(ending.map((session) => teardown(session).catch(() => undefined)));
282
+ }
@@ -1,5 +1,16 @@
1
+ import { type ProjectCandidate } from '@reticlehq/core/artifacts';
1
2
  import { type ProjectId } from '@reticlehq/core';
2
3
  import { type ArtifactRoot } from './artifact-root.js';
4
+ /**
5
+ * Every project this machine knows the directory of: discovered `.reticle.json` files first, then
6
+ * the user-level registry, then the dev servers the build plugin announced. Read on each call, since
7
+ * `init` can run in another terminal while the daemon is up.
8
+ *
9
+ * Exported because sync must cover exactly the projects runs are written into. It kept its own list
10
+ * without the announced dev servers, so a project known only through its running dev server had its
11
+ * runs written to its `.reticle/runs/` and never sent, with nothing reporting a problem.
12
+ */
13
+ export declare function knownProjectCandidates(): ProjectCandidate[];
3
14
  /**
4
15
  * The directory of the project a page announced, or undefined when this machine does not know it.
5
16
  *
@@ -10,4 +21,18 @@ import { type ArtifactRoot } from './artifact-root.js';
10
21
  * own project id.
11
22
  */
12
23
  export declare function projectDirectoryFor(projectId: string | undefined): string | undefined;
13
- export declare function artifactRootResolver(daemonRoot: string): (projectId: ProjectId | undefined, origin?: string) => ArtifactRoot;
24
+ /** Where this resolver gets its IO answers. The defaults are the production ones. */
25
+ export interface ArtifactRootResolverDeps {
26
+ /** Every project this machine knows about. Defaults to discovery + the user registry. */
27
+ candidates?: () => ProjectCandidate[];
28
+ /** Whether the daemon's own directory is a Reticle project. Defaults to looking at disk. */
29
+ daemonIsProject?: (daemonRoot: string) => boolean;
30
+ /**
31
+ * The directory of the dev server that served a page origin, or undefined when unobservable.
32
+ * Required, not defaulted: the production answer reads daemon state and shells out, which is the
33
+ * wiring layer's business (`portal/session/serving-directory.ts`), and a resolver built without it
34
+ * would silently keep sending every worktree's artifacts to the primary checkout.
35
+ */
36
+ servingDirectory: (origin: string) => string | undefined;
37
+ }
38
+ export declare function artifactRootResolver(daemonRoot: string, deps: ArtifactRootResolverDeps): (projectId: ProjectId | undefined, origin?: string) => ArtifactRoot;
@@ -7,12 +7,21 @@
7
7
  *
8
8
  * It lives here rather than in the daemon bootstrap because it is a subject, not wiring — and
9
9
  * because the bootstrap is at its line cap, which is the cohesion signal that rule doing its job.
10
+ *
11
+ * ## Why `artifactRootResolver` takes a `deps`
12
+ *
13
+ * Both of its IO answers come from the REAL disk and the REAL `process.cwd()`: which projects this
14
+ * machine knows, and whether the daemon's own directory declares one. That left the file untestable
15
+ * at the level where its defect actually lives — the `unmatchedRoot` specs next door hand a
16
+ * `daemonProjectId` straight in, so a mistake in READING it would leave every one of them green
17
+ * while the worktree bug reproduced unchanged. The defaults are the production answers; the seam is
18
+ * what lets a test say "these are the projects on this machine" without one.
10
19
  */
11
- import { existsSync, readFileSync } from 'node:fs';
12
- import { dirname, join } from 'node:path';
20
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
21
+ import { dirname, isAbsolute, join, relative } from 'node:path';
13
22
  import { homedir } from 'node:os';
14
23
  import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, } from '@reticlehq/core/artifacts';
15
- import { ReticleDir } from '@reticlehq/core';
24
+ import { DevServerEntrySchema, ReticleDir, ReticleEnv, devServerRegistryPort, } from '@reticlehq/core';
16
25
  import { discoverProjectConfigs, hasProjectConfig, } from '../../command/cli/config/config-discovery.js';
17
26
  import { log } from '../../log.js';
18
27
  import { ArtifactRootReason, projectCandidatesFrom, resolveArtifactRoot, unmatchedRoot, } from './artifact-root.js';
@@ -34,12 +43,47 @@ function daemonSitsInAProject(daemonRoot) {
34
43
  return false;
35
44
  }
36
45
  }
46
+ /**
47
+ * The project a candidate list says lives at the daemon's own directory.
48
+ *
49
+ * Not the same question as `daemonSitsInAProject`: that one asks whether the tree invited Reticle in
50
+ * at all (either mark counts, including a bare `.reticle/` from a version that predates the config
51
+ * file), and this one asks WHICH project it is. The difference is the whole of #1244 — a daemon in
52
+ * repo A serving an app from a git worktree B belongs to A and is being asked about B.
53
+ *
54
+ * Takes the candidates rather than gathering them, so the answer is a function of one list and a test
55
+ * can supply it. Read from that same list the resolver already builds, so there is one answer to
56
+ * "where does this project live" rather than a second `.reticle.json` parser here. A daemon whose
57
+ * root was passed explicitly (`--root`, or an embedder's own `reticleRoot`) is not in that list and
58
+ * answers undefined, which is the safe direction: it cannot prove the named project is its own, so it
59
+ * declines the daemon root and the session goes to the unmatched bucket instead.
60
+ */
61
+ function daemonOwnProjectId(daemonRoot, candidates) {
62
+ const directory = dirname(daemonRoot);
63
+ const own = candidates.find((candidate) => candidate.directory === directory)?.projectId;
64
+ // A monorepo root carries the id of the app `init --app` wired, as a pointer for an agent started
65
+ // there. When an app inside the root declares the same id, the root is pointing at it, not being
66
+ // it: answering with the id handed a second checkout's flows to this root with no warning.
67
+ const pointer = candidates.some((candidate) => candidate.projectId === own &&
68
+ candidate.directory !== directory &&
69
+ isInside(directory, candidate.directory));
70
+ return pointer ? undefined : own;
71
+ }
72
+ /** Is `dir` strictly inside `root`? */
73
+ function isInside(root, dir) {
74
+ const rel = relative(root, dir);
75
+ return 0 < rel.length && !rel.startsWith('..') && !isAbsolute(rel);
76
+ }
37
77
  /**
38
78
  * Every project this machine knows the directory of: discovered `.reticle.json` files first, then
39
- * the user-level registry. Read on each call, since `init` can run in another terminal while the
40
- * daemon is up.
79
+ * the user-level registry, then the dev servers the build plugin announced. Read on each call, since
80
+ * `init` can run in another terminal while the daemon is up.
81
+ *
82
+ * Exported because sync must cover exactly the projects runs are written into. It kept its own list
83
+ * without the announced dev servers, so a project known only through its running dev server had its
84
+ * runs written to its `.reticle/runs/` and never sent, with nothing reporting a problem.
41
85
  */
42
- function knownProjectCandidates() {
86
+ export function knownProjectCandidates() {
43
87
  let registry = emptyProjectRegistry();
44
88
  try {
45
89
  const path = join(homedir(), ReticleDir.ROOT, PROJECT_REGISTRY_FILE);
@@ -58,7 +102,35 @@ function knownProjectCandidates() {
58
102
  catch {
59
103
  // Same reasoning: a diagnostic search that throws must not take a tool call with it.
60
104
  }
61
- return projectCandidatesFrom(discovery, registry);
105
+ return projectCandidatesFrom(discovery, registry, announcedDevServers());
106
+ }
107
+ /**
108
+ * The dev servers the build plugin announced, with the project id and root each serves. Read from
109
+ * the same state directory the plugin writes to. Unreadable or malformed entries are skipped: a stale
110
+ * announcement only ever names a directory that really was that project.
111
+ */
112
+ function announcedDevServers() {
113
+ const override = process.env[ReticleEnv.STATE_DIR];
114
+ const dir = override !== undefined && 0 < override.length ? override : join(homedir(), ReticleDir.ROOT);
115
+ const servers = [];
116
+ try {
117
+ for (const name of readdirSync(dir)) {
118
+ if (null === devServerRegistryPort(name))
119
+ continue;
120
+ try {
121
+ const parsed = DevServerEntrySchema.safeParse(JSON.parse(readFileSync(join(dir, name), 'utf8')));
122
+ if (parsed.success)
123
+ servers.push({ projectId: parsed.data.projectId, root: parsed.data.root });
124
+ }
125
+ catch {
126
+ /* one bad announcement does not hide the others */
127
+ }
128
+ }
129
+ }
130
+ catch {
131
+ /* no state directory yet: nothing has been announced */
132
+ }
133
+ return servers;
62
134
  }
63
135
  /**
64
136
  * The directory of the project a page announced, or undefined when this machine does not know it.
@@ -79,14 +151,41 @@ export function projectDirectoryFor(projectId) {
79
151
  // them would read the wrong app's package.json and prescribe its packages to the other.
80
152
  return 1 === directories.size ? [...directories][0] : undefined;
81
153
  }
82
- export function artifactRootResolver(daemonRoot) {
83
- const daemonIsProject = daemonSitsInAProject(daemonRoot);
154
+ export function artifactRootResolver(daemonRoot, deps) {
155
+ const candidates = deps.candidates ?? knownProjectCandidates;
156
+ const sitsInAProject = deps.daemonIsProject ?? daemonSitsInAProject;
157
+ const { servingDirectory } = deps;
158
+ const daemonIsProject = sitsInAProject(daemonRoot);
159
+ // Resolved beside it and ONCE, for the same reason: a directory's `.reticle.json` does not change
160
+ // between two tabs connecting. Only read when the daemon really is in a project, since it is the
161
+ // sole thing that can license a write into the daemon's own tree.
162
+ const daemonProjectId = daemonIsProject
163
+ ? daemonOwnProjectId(daemonRoot, candidates())
164
+ : undefined;
165
+ /** Said once per project, reason and folder: one session's calls repeated it 16 times in 3s. */
166
+ const told = new Set();
167
+ // ponytail: asked once per origin (it shells out); a dev server moved to another folder on the
168
+ // same port is named stale until the daemon restarts.
169
+ const servedFrom = new Map();
170
+ const servingOf = (origin) => {
171
+ if (!servedFrom.has(origin))
172
+ servedFrom.set(origin, servingDirectory(origin));
173
+ return servedFrom.get(origin);
174
+ };
84
175
  return (projectId, origin) => {
85
- const resolved = resolveArtifactRoot({
86
- projectId,
87
- candidates: knownProjectCandidates(),
88
- daemonRoot,
89
- });
176
+ const known = candidates();
177
+ let resolved = resolveArtifactRoot({ projectId, candidates: known, daemonRoot });
178
+ // Several checkouts declare this project — every git worktree carries the same committed
179
+ // `.reticle.json`. Only now is the serving process looked up: it shells out, and the tie is the
180
+ // one case where where the page was SERVED from is the deciding fact.
181
+ if (resolved.reason === ArtifactRootReason.AMBIGUOUS && origin !== undefined) {
182
+ resolved = resolveArtifactRoot({
183
+ projectId,
184
+ candidates: known,
185
+ daemonRoot,
186
+ servingDirectory: servingOf(origin),
187
+ });
188
+ }
90
189
  if (resolved.reason === ArtifactRootReason.MATCHED_PROJECT)
91
190
  return resolved;
92
191
  // Could not name the project. The old code wrote into the daemon's directory anyway and said
@@ -98,19 +197,29 @@ export function artifactRootResolver(daemonRoot) {
98
197
  daemonRoot,
99
198
  daemonIsProject,
100
199
  home: homedir(),
200
+ ...(daemonProjectId === undefined ? {} : { daemonProjectId }),
101
201
  ...(projectId === undefined ? {} : { projectId }),
102
202
  // Only reached when no project id survives the guard. Without it every app that never
103
203
  // stamped one shares a single directory -- and that directory holds the durable half, so one
104
204
  // app's learned expectations become another's.
105
205
  ...(origin === undefined ? {} : { origin }),
106
206
  });
107
- if (root !== daemonRoot) {
207
+ const key = `${projectId ?? ''}\u0000${resolved.reason}\u0000${root}`;
208
+ if (root !== daemonRoot && !told.has(key)) {
209
+ told.add(key);
108
210
  log('artifact_root_unmatched', {
109
211
  projectId: projectId ?? null,
212
+ // The other half of the comparison, so a reader can tell "the daemon was a guest" from
213
+ // "the daemon is in a DIFFERENT project" without reading the config files by hand.
214
+ daemonProjectId: daemonProjectId ?? null,
110
215
  reason: resolved.reason,
111
216
  root,
217
+ ...(resolved.candidates === undefined ? {} : { candidates: resolved.candidates }),
112
218
  });
113
219
  }
114
- return { ...resolved, root };
220
+ // Named so the agent is told WHICH checkout's work is stranded: a clone serving the app was
221
+ // never named, only the primary checkout's folders.
222
+ const serving = origin === undefined ? undefined : servingOf(origin);
223
+ return { ...resolved, root, ...(serving === undefined ? {} : { serving }) };
115
224
  };
116
225
  }
@@ -1,3 +1,4 @@
1
+ import { UNMATCHED_SUBDIR } from './dir/reticle-dir.js';
1
2
  import { type ProjectCandidate, type ProjectId, type ProjectRegistry } from '@reticlehq/core';
2
3
  import type { ConfigDiscovery } from '../../command/cli/config/config-discovery.js';
3
4
  /**
@@ -43,6 +44,13 @@ interface ArtifactRootQuery {
43
44
  candidates: readonly ProjectCandidate[];
44
45
  /** Where artifacts go when the project cannot be identified. Already a `.reticle` path. */
45
46
  daemonRoot: string;
47
+ /**
48
+ * The directory the dev server that served this page runs in, when the caller could observe it.
49
+ * Only consulted to break an AMBIGUOUS tie: every git worktree carries the same committed
50
+ * `.reticle.json`, so the projectId cannot tell them apart, but the serving process runs inside
51
+ * exactly one of them.
52
+ */
53
+ servingDirectory?: string | undefined;
46
54
  }
47
55
  export interface ArtifactRoot {
48
56
  /** Absolute path to the `.reticle` directory. Always present, on every branch. */
@@ -53,6 +61,8 @@ export interface ArtifactRoot {
53
61
  * needs to name them; a caller that does not can ignore the field.
54
62
  */
55
63
  candidates?: string[];
64
+ /** The folder whose dev server served the page, when the evidence went unmatched and it is known. */
65
+ serving?: string;
56
66
  }
57
67
  /**
58
68
  * Resolve the artifact root for one session.
@@ -68,9 +78,11 @@ export declare function resolveArtifactRoot(query: ArtifactRootQuery): ArtifactR
68
78
  * is a cache of something `init` saw once. Order only decides which duplicate survives dedupe, and
69
79
  * the one confirmed a moment ago is the better survivor.
70
80
  */
71
- export declare function projectCandidatesFrom(discovery: ConfigDiscovery, registry: ProjectRegistry): ProjectCandidate[];
72
- /** Where evidence goes when no project could be named and the daemon is a guest in this tree. */
73
- export declare const UNMATCHED_SUBDIR = "unmatched";
81
+ export declare function projectCandidatesFrom(discovery: ConfigDiscovery, registry: ProjectRegistry, devServers?: readonly {
82
+ projectId?: string | undefined;
83
+ root: string;
84
+ }[]): ProjectCandidate[];
85
+ export { UNMATCHED_SUBDIR };
74
86
  /**
75
87
  * The fallback root for a session whose project could not be resolved.
76
88
  *
@@ -83,20 +95,47 @@ export declare const UNMATCHED_SUBDIR = "unmatched";
83
95
  * Two cases, and only one of them was ever the intended behaviour:
84
96
  *
85
97
  * - the daemon is sitting IN a Reticle project (a developer who ran `reticle serve` in their own
86
- * app). Its root is the right answer, and it is the case the old fallback was written for.
87
- * - the daemon is a guest — no `.reticle.json`, no `.reticle/` already there. Then the evidence
88
- * goes to the user's own `~/.reticle/unmatched/<projectId>`, which is Reticle's to write.
98
+ * app), and the session is asking about THAT project. Its root is the right answer, and it is
99
+ * the case the old fallback was written for.
100
+ * - otherwise — a guest in the tree, or a daemon in its own checkout serving a DIFFERENT project.
101
+ * Then the evidence goes to the user's own `~/.reticle/unmatched/<projectId>`, which is
102
+ * Reticle's to write.
89
103
  *
90
104
  * It is never dropped. A verdict with nowhere to live is a worse failure than one in an unexpected
91
105
  * place, and the caller says out loud where it went.
92
106
  *
107
+ * ## The second case in that list used to be missing, and it is not an edge case
108
+ *
109
+ * `daemonIsProject` answers "is the daemon sitting in SOME Reticle project". The guard below read it
110
+ * as "this session's project", and those are different questions whenever the two ids differ. Run
111
+ * the daemon in repo A, then connect an app from a git worktree B of that same repo: B has its own
112
+ * `projectId` in its own `.reticle.json`, and discovery cannot see it — it walks out from A's
113
+ * `process.cwd()` and never crosses into a sibling checkout — so this function is reached with a
114
+ * NAMED project and the daemon's root came back anyway. B's intents, flows and runs were written
115
+ * into A's checkout, and the returned path said A, so nothing looked wrong.
116
+ *
117
+ * A real identity always wins. The daemon's root is only the answer when it is that identity's own
118
+ * root, or when no identity was named at all.
119
+ *
93
120
  * `projectId` arrives in HELLO from the page, so it is untrusted input on a path join and is held
94
- * to one safe segment — the same guard session ids and flow names already pass.
121
+ * to one safe segment — the same guard session ids and flow names already pass. `daemonProjectId`
122
+ * is read from the daemon's own `.reticle.json` and gets the same treatment, because equality
123
+ * against a guarded value is only as strong as the weaker side.
95
124
  */
96
125
  export declare function unmatchedRoot(query: {
97
126
  daemonRoot: string;
98
127
  /** Whether the daemon's own directory is a Reticle project — an IO question, answered by the caller. */
99
128
  daemonIsProject: boolean;
129
+ /**
130
+ * The daemon's OWN projectId, when its directory declares one. Absent means the daemon cannot
131
+ * prove which project it belongs to, so a named project that is not demonstrably its own declines.
132
+ *
133
+ * A plain `string` rather than a `ProjectId`, because it arrives from a `ProjectCandidate` — the
134
+ * registry's view of a `.reticle.json` — and the brand is only minted where the id is read out of
135
+ * that file for this daemon. Comparing it against a branded session id is unaffected: the brand is
136
+ * a `string`, and the guard below is what actually holds it to one safe path segment.
137
+ */
138
+ daemonProjectId?: string | undefined;
100
139
  /** The user's home directory. Passed in rather than read, so this stays pure. */
101
140
  home: string;
102
141
  projectId?: ProjectId | undefined;
@@ -106,4 +145,3 @@ export declare function unmatchedRoot(query: {
106
145
  */
107
146
  origin?: string | undefined;
108
147
  }): string;
109
- export {};
@@ -1,4 +1,5 @@
1
- import { join } from 'node:path';
1
+ import { isAbsolute, join, relative, sep } from 'node:path';
2
+ import { UNMATCHED_SUBDIR } from './dir/reticle-dir.js';
2
3
  import { SAFE_SEGMENT_PATTERN, ReticleDir, fnv1a, projectCandidates, } from '@reticlehq/core';
3
4
  /**
4
5
  * Which `.reticle/` a session's artifacts belong in.
@@ -51,6 +52,10 @@ export function resolveArtifactRoot(query) {
51
52
  return { root: daemonRoot, reason: ArtifactRootReason.NO_MATCH };
52
53
  }
53
54
  if (matches.length > 1) {
55
+ const served = checkoutContaining(matches, query.servingDirectory);
56
+ if (served !== undefined) {
57
+ return { root: join(served, ReticleDir.ROOT), reason: ArtifactRootReason.MATCHED_PROJECT };
58
+ }
54
59
  return {
55
60
  root: daemonRoot,
56
61
  reason: ArtifactRootReason.AMBIGUOUS,
@@ -66,6 +71,24 @@ export function resolveArtifactRoot(query) {
66
71
  }
67
72
  return { root: join(directory, ReticleDir.ROOT), reason: ArtifactRootReason.MATCHED_PROJECT };
68
73
  }
74
+ /**
75
+ * The checkout the serving directory sits in, or undefined when it is in none of them.
76
+ *
77
+ * Deepest wins, because a worktree can live INSIDE the primary checkout (`.claude/worktrees/x`) and
78
+ * then sits under both; the deeper one is the one the server actually runs in. `relative` rather
79
+ * than a string prefix, so `/wt/pr-77` is not read as inside `/wt/pr-7`, and on every platform.
80
+ */
81
+ function checkoutContaining(matches, servingDirectory) {
82
+ if (servingDirectory === undefined || 0 === servingDirectory.length)
83
+ return undefined;
84
+ const inside = matches
85
+ .map((candidate) => candidate.directory)
86
+ .filter((directory) => {
87
+ const rel = relative(directory, servingDirectory);
88
+ return '' === rel || ('..' !== rel && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
89
+ });
90
+ return inside.sort((a, b) => b.length - a.length)[0];
91
+ }
69
92
  /**
70
93
  * One entry per directory.
71
94
  *
@@ -92,14 +115,23 @@ function dedupeByDirectory(matches) {
92
115
  * is a cache of something `init` saw once. Order only decides which duplicate survives dedupe, and
93
116
  * the one confirmed a moment ago is the better survivor.
94
117
  */
95
- export function projectCandidatesFrom(discovery, registry) {
118
+ export function projectCandidatesFrom(discovery, registry, devServers = []) {
96
119
  const discovered = discovery.found.flatMap((config) => config.projectId === undefined || 0 === config.projectId.length
97
120
  ? []
98
121
  : [{ projectId: config.projectId, directory: config.directory }]);
99
- return [...discovered, ...projectCandidates(registry)];
122
+ const known = [...discovered, ...projectCandidates(registry)];
123
+ // A dev server's own announcement of its project id and root, ONLY for an id nothing above names.
124
+ // It is the one source that knows an app wired by the build plugin alone (no `.reticle.json`, never
125
+ // `init`-ed), whose runs otherwise went to `unmatched/`, which nothing syncs. It fills gaps and never
126
+ // competes: `init` at a monorepo root names the root while the dev server names the app's subfolder,
127
+ // and counting both refused a project that resolved fine before.
128
+ const named = new Set(known.map((candidate) => candidate.projectId));
129
+ const announced = devServers.flatMap((server) => server.projectId === undefined || 0 === server.projectId.length || named.has(server.projectId)
130
+ ? []
131
+ : [{ projectId: server.projectId, directory: server.root }]);
132
+ return [...known, ...announced];
100
133
  }
101
- /** Where evidence goes when no project could be named and the daemon is a guest in this tree. */
102
- export const UNMATCHED_SUBDIR = 'unmatched';
134
+ export { UNMATCHED_SUBDIR };
103
135
  /** What a project with NO identity at all is called on disk. Never blank, so the path is always real. */
104
136
  const UNNAMED_PROJECT = 'unnamed';
105
137
  /**
@@ -132,23 +164,70 @@ const ORIGIN_BUCKET_PREFIX = 'origin-';
132
164
  * Two cases, and only one of them was ever the intended behaviour:
133
165
  *
134
166
  * - the daemon is sitting IN a Reticle project (a developer who ran `reticle serve` in their own
135
- * app). Its root is the right answer, and it is the case the old fallback was written for.
136
- * - the daemon is a guest — no `.reticle.json`, no `.reticle/` already there. Then the evidence
137
- * goes to the user's own `~/.reticle/unmatched/<projectId>`, which is Reticle's to write.
167
+ * app), and the session is asking about THAT project. Its root is the right answer, and it is
168
+ * the case the old fallback was written for.
169
+ * - otherwise — a guest in the tree, or a daemon in its own checkout serving a DIFFERENT project.
170
+ * Then the evidence goes to the user's own `~/.reticle/unmatched/<projectId>`, which is
171
+ * Reticle's to write.
138
172
  *
139
173
  * It is never dropped. A verdict with nowhere to live is a worse failure than one in an unexpected
140
174
  * place, and the caller says out loud where it went.
141
175
  *
176
+ * ## The second case in that list used to be missing, and it is not an edge case
177
+ *
178
+ * `daemonIsProject` answers "is the daemon sitting in SOME Reticle project". The guard below read it
179
+ * as "this session's project", and those are different questions whenever the two ids differ. Run
180
+ * the daemon in repo A, then connect an app from a git worktree B of that same repo: B has its own
181
+ * `projectId` in its own `.reticle.json`, and discovery cannot see it — it walks out from A's
182
+ * `process.cwd()` and never crosses into a sibling checkout — so this function is reached with a
183
+ * NAMED project and the daemon's root came back anyway. B's intents, flows and runs were written
184
+ * into A's checkout, and the returned path said A, so nothing looked wrong.
185
+ *
186
+ * A real identity always wins. The daemon's root is only the answer when it is that identity's own
187
+ * root, or when no identity was named at all.
188
+ *
142
189
  * `projectId` arrives in HELLO from the page, so it is untrusted input on a path join and is held
143
- * to one safe segment — the same guard session ids and flow names already pass.
190
+ * to one safe segment — the same guard session ids and flow names already pass. `daemonProjectId`
191
+ * is read from the daemon's own `.reticle.json` and gets the same treatment, because equality
192
+ * against a guarded value is only as strong as the weaker side.
144
193
  */
145
194
  export function unmatchedRoot(query) {
146
- if (query.daemonIsProject)
195
+ if (query.daemonIsProject && isTheDaemonsOwnProject(query.daemonProjectId, query.projectId))
147
196
  return query.daemonRoot;
148
197
  const id = query.projectId ?? '';
149
198
  const named = SAFE_SEGMENT_PATTERN.test(id) && !id.includes('..');
150
199
  return join(query.home, ReticleDir.ROOT, UNMATCHED_SUBDIR, named ? id : unnamedSegment(query.origin));
151
200
  }
201
+ /**
202
+ * May the daemon write this session's artifacts into its own `.reticle/`?
203
+ *
204
+ * Two ways to qualify, and the difference between them is the whole bug: belonging to the directory
205
+ * is not the same as owning it.
206
+ *
207
+ * - Nobody named a project (a pre-2.0 SDK sends no projectId). There is no identity to disagree
208
+ * with, and the daemon's own root is the only thing on offer.
209
+ * - The named project IS the daemon's own, and the daemon can prove it — `.reticle.json` sits in
210
+ * its directory and declares the same id. Both sides go through the same segment guard, because
211
+ * an equality between a guarded value and an unguarded one is only as strong as the loose side.
212
+ *
213
+ * A NAMED project the daemon cannot match declines, including when it has a `.reticle/` but no
214
+ * readable id. Declining costs the session its home directory and buys the guarantee that one
215
+ * checkout never receives another's ledger; the alternative is a wrong answer that reports success.
216
+ *
217
+ * The two ids are REQUIRED parameters rather than one object, and that is load-bearing. Taking a
218
+ * partial object let a caller omit a field and still compile — and an omitted `projectId` reads as
219
+ * "nobody named a project", which answers `true` and puts the session straight back into the daemon's
220
+ * checkout. That is the original defect, reachable by a typo rather than by a decision.
221
+ */
222
+ function isTheDaemonsOwnProject(daemonProjectId, projectId) {
223
+ if (projectId === undefined || 0 === projectId.length)
224
+ return true;
225
+ if (daemonProjectId === undefined || 0 === daemonProjectId.length)
226
+ return false;
227
+ return (daemonProjectId === projectId &&
228
+ SAFE_SEGMENT_PATTERN.test(daemonProjectId) &&
229
+ !daemonProjectId.includes('..'));
230
+ }
152
231
  /** Hashed, not spelled: an origin carries a host and a port, and neither belongs in a path segment. */
153
232
  function unnamedSegment(origin) {
154
233
  if (origin === undefined || 0 === origin.trim().length)