@reticlehq/server 3.7.0 → 3.8.1

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 (578) hide show
  1. package/SKILL.md +59 -68
  2. package/bin/reticle.js +9 -2
  3. package/dist/command/cli/adhoc-suite.js +1 -1
  4. package/dist/command/cli/adhoc-verdict.d.ts +12 -0
  5. package/dist/command/cli/adhoc-verdict.js +62 -4
  6. package/dist/command/cli/answers/gate-hook-message.d.ts +2 -0
  7. package/dist/command/cli/answers/gate-hook-message.js +5 -1
  8. package/dist/command/cli/auth/cloud-session.d.ts +11 -19
  9. package/dist/command/cli/auth/cloud-session.js +23 -0
  10. package/dist/command/cli/auth/device-flow.d.ts +3 -37
  11. package/dist/command/cli/cli-doctor.js +66 -21
  12. package/dist/command/cli/cli-flow-commands.js +53 -11
  13. package/dist/command/cli/cli-kill.d.ts +8 -0
  14. package/dist/command/cli/cli-kill.js +43 -3
  15. package/dist/command/cli/cli-parse-feedback.d.ts +4 -0
  16. package/dist/command/cli/cli-parse-feedback.js +13 -1
  17. package/dist/command/cli/cli-parse-grammar.d.ts +1 -2
  18. package/dist/command/cli/cli-parse-grammar.js +1 -6
  19. package/dist/command/cli/cli-parse-serve.d.ts +24 -0
  20. package/dist/command/cli/cli-parse-serve.js +124 -0
  21. package/dist/command/cli/cli-parse.d.ts +12 -11
  22. package/dist/command/cli/cli-parse.js +100 -123
  23. package/dist/command/cli/cli-usage.d.ts +0 -11
  24. package/dist/command/cli/cli-usage.js +43 -16
  25. package/dist/command/cli/cli-verify.d.ts +1 -1
  26. package/dist/command/cli/cli-verify.js +29 -14
  27. package/dist/command/cli/cloud-cli.js +27 -9
  28. package/dist/command/cli/cloud-kit.js +3 -3
  29. package/dist/command/cli/cloud-link.d.ts +1 -20
  30. package/dist/command/cli/cloud-link.js +2 -0
  31. package/dist/command/cli/cloud-login.d.ts +6 -2
  32. package/dist/command/cli/cloud-login.js +13 -41
  33. package/dist/command/cli/config/checkout.d.ts +10 -0
  34. package/dist/command/cli/config/checkout.js +78 -0
  35. package/dist/command/cli/doctor/doctor-agent-link.d.ts +23 -0
  36. package/dist/command/cli/doctor/doctor-agent-link.js +28 -0
  37. package/dist/command/cli/doctor/doctor-coverage-line.js +1 -7
  38. package/dist/command/cli/doctor/doctor-rows.d.ts +4 -0
  39. package/dist/command/cli/doctor/doctor-rows.js +4 -0
  40. package/dist/command/cli/drive/drive-attach.d.ts +5 -0
  41. package/dist/command/cli/drive/drive-attach.js +21 -1
  42. package/dist/command/cli/hook/codex-transcript.d.ts +20 -0
  43. package/dist/command/cli/hook/codex-transcript.js +224 -0
  44. package/dist/command/cli/hook/episode-structure.d.ts +24 -0
  45. package/dist/command/cli/hook/episode-structure.js +138 -0
  46. package/dist/command/cli/hook/hook-command.d.ts +9 -0
  47. package/dist/command/cli/hook/hook-command.js +211 -0
  48. package/dist/command/cli/hook/hook-fast.d.ts +31 -0
  49. package/dist/command/cli/hook/hook-fast.js +136 -0
  50. package/dist/command/cli/hook/prompt-hook.d.ts +30 -0
  51. package/dist/command/cli/hook/prompt-hook.js +199 -0
  52. package/dist/command/cli/hook/tool-hook.d.ts +21 -0
  53. package/dist/command/cli/hook/tool-hook.js +259 -0
  54. package/dist/command/cli/launch/cli-launch.d.ts +2 -0
  55. package/dist/command/cli/launch/cli-launch.js +22 -2
  56. package/dist/command/cli/lifecycle/daemon-lifecycle.js +28 -2
  57. package/dist/command/cli/mcp-command.d.ts +9 -0
  58. package/dist/command/cli/mcp-command.js +127 -41
  59. package/dist/command/cli/open-navigate.d.ts +43 -0
  60. package/dist/command/cli/open-navigate.js +85 -0
  61. package/dist/command/cli/ports/port-holder.d.ts +16 -4
  62. package/dist/command/cli/ports/port-holder.js +58 -12
  63. package/dist/command/cli/ports/resolve/cli-port.js +18 -2
  64. package/dist/command/cli/project-name.d.ts +1 -7
  65. package/dist/command/cli/project-name.js +1 -0
  66. package/dist/command/cli/report-command.d.ts +8 -2
  67. package/dist/command/cli/report-command.js +95 -18
  68. package/dist/command/cli/status-fields.d.ts +9 -2
  69. package/dist/command/cli/status-fields.js +148 -4
  70. package/dist/command/cli/try-command.d.ts +3 -4
  71. package/dist/command/cli/try-command.js +6 -11
  72. package/dist/command/cli/verify-results.d.ts +4 -1
  73. package/dist/command/cli/verify-results.js +3 -1
  74. package/dist/command/cli.d.ts +1 -7
  75. package/dist/command/cli.js +79 -160
  76. package/dist/command/daemon/daemon-resilience.d.ts +6 -1
  77. package/dist/command/daemon/daemon-resilience.js +31 -18
  78. package/dist/command/daemon/daemon.d.ts +11 -0
  79. package/dist/command/daemon/daemon.js +70 -2
  80. package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +2 -0
  81. package/dist/command/daemon/lifetime/daemon-usefulness.js +13 -2
  82. package/dist/command/daemon/wake-decision.js +4 -2
  83. package/dist/command/daemon/win32-escape.d.ts +32 -0
  84. package/dist/command/daemon/win32-escape.js +119 -0
  85. package/dist/command/setup/agent-configs.d.ts +9 -0
  86. package/dist/command/setup/agent-configs.js +9 -0
  87. package/dist/command/setup/auto-approve.d.ts +10 -3
  88. package/dist/command/setup/auto-approve.js +51 -3
  89. package/dist/command/setup/bringup/dev-server-files.d.ts +1 -9
  90. package/dist/command/setup/bringup/dev-server-wait.js +5 -1
  91. package/dist/command/setup/bringup/ensure-daemon.js +4 -7
  92. package/dist/command/setup/init/init-host.js +6 -0
  93. package/dist/command/setup/init/init-port.js +5 -4
  94. package/dist/command/setup/init/init-runtime.js +7 -1
  95. package/dist/command/setup/node-effects.d.ts +8 -0
  96. package/dist/command/setup/node-effects.js +36 -4
  97. package/dist/command/setup/probe/dev-port.d.ts +26 -0
  98. package/dist/command/setup/probe/dev-port.js +117 -0
  99. package/dist/command/setup/probe/page-probe.d.ts +1 -1
  100. package/dist/command/setup/probe/page-probe.js +11 -3
  101. package/dist/command/setup/remaining-steps.d.ts +0 -7
  102. package/dist/command/setup/remaining-steps.js +3 -2
  103. package/dist/command/setup/run-setup.d.ts +11 -0
  104. package/dist/command/setup/run-setup.js +33 -5
  105. package/dist/command/setup/setup-command.d.ts +2 -0
  106. package/dist/command/setup/setup-command.js +22 -4
  107. package/dist/command/update/updater.d.ts +1 -6
  108. package/dist/command/update/updater.js +4 -5
  109. package/dist/command/update/version-seen.d.ts +24 -0
  110. package/dist/command/update/version-seen.js +65 -0
  111. package/dist/command/version/peer-announce.d.ts +9 -1
  112. package/dist/command/version/peer-announce.js +25 -2
  113. package/dist/command/version/sdk-fix.d.ts +5 -6
  114. package/dist/command/version/sdk-fix.js +17 -32
  115. package/dist/command/version/version-skew.d.ts +16 -1
  116. package/dist/command/version/version-skew.js +84 -10
  117. package/dist/features/crawl/crawl-tools.js +2 -2
  118. package/dist/features/crawl/crawl.js +2 -0
  119. package/dist/features/exhaust/ledger.d.ts +15 -96
  120. package/dist/features/exhaust/ledger.js +3 -3
  121. package/dist/features/harness/drive-runs.d.ts +28 -4
  122. package/dist/features/harness/drive-runs.js +99 -7
  123. package/dist/features/harness/drivers.d.ts +1 -1
  124. package/dist/features/harness/drivers.js +1 -1
  125. package/dist/features/harness/harness.d.ts +5 -0
  126. package/dist/features/harness/harness.js +24 -12
  127. package/dist/features/harness/platform/platform-drives.d.ts +7 -1
  128. package/dist/features/harness/platform/platform-drives.js +12 -7
  129. package/dist/features/harness/platform/refusal.d.ts +1 -1
  130. package/dist/features/harness/platform/refusal.js +4 -4
  131. package/dist/features/harness/platform/remote-drive.js +70 -28
  132. package/dist/features/harness/platform/server-driver.js +22 -1
  133. package/dist/features/harness/platform-config.d.ts +1 -10
  134. package/dist/features/harness/platform-config.js +0 -20
  135. package/dist/features/license/license.d.ts +2 -14
  136. package/dist/features/visual/step-shots.d.ts +33 -1
  137. package/dist/features/visual/step-shots.js +155 -1
  138. package/dist/features/visual/visual-tools.js +12 -4
  139. package/dist/hooks/coding-agents.d.ts +6 -0
  140. package/dist/hooks/coding-agents.js +24 -3
  141. package/dist/hooks/episode-stamp.d.ts +32 -0
  142. package/dist/hooks/episode-stamp.js +61 -0
  143. package/dist/hooks/hook-commands.js +19 -9
  144. package/dist/index.d.ts +5 -1
  145. package/dist/index.js +66 -24
  146. package/dist/judgement/app-graph/journal-steps.d.ts +8 -0
  147. package/dist/judgement/app-graph/journal-steps.js +92 -0
  148. package/dist/judgement/capsule/capsule-store.d.ts +2 -39
  149. package/dist/judgement/domain/domain-model.js +1 -10
  150. package/dist/judgement/domain/domain-tools.js +4 -2
  151. package/dist/judgement/runs/artifact/run-store.d.ts +28 -8
  152. package/dist/judgement/runs/artifact/run-store.js +153 -28
  153. package/dist/judgement/runs/default-run-id.d.ts +3 -1
  154. package/dist/judgement/runs/default-run-id.js +15 -3
  155. package/dist/judgement/runs/drive-run.d.ts +3 -3
  156. package/dist/judgement/runs/drive-run.js +11 -7
  157. package/dist/judgement/runs/run-ledger.d.ts +21 -0
  158. package/dist/judgement/runs/run-ledger.js +143 -0
  159. package/dist/judgement/runs/runner-port.d.ts +1 -1
  160. package/dist/judgement/runs/runner-port.js +8 -2
  161. package/dist/judgement/runs/verification-sync.d.ts +4 -3
  162. package/dist/judgement/runs/verification-sync.js +27 -5
  163. package/dist/language/flows/affected-tools.js +2 -1
  164. package/dist/language/flows/annotate-notes/annotate.js +8 -3
  165. package/dist/language/flows/annotate-tools.js +1 -1
  166. package/dist/language/flows/change/affected.d.ts +8 -0
  167. package/dist/language/flows/change/affected.js +20 -7
  168. package/dist/language/flows/decision.js +19 -4
  169. package/dist/language/flows/fields/flow-secret-field.d.ts +9 -0
  170. package/dist/language/flows/fields/flow-secret-field.js +67 -5
  171. package/dist/language/flows/flow-anchor.d.ts +5 -0
  172. package/dist/language/flows/flow-anchor.js +4 -0
  173. package/dist/language/flows/flow-cross-step.d.ts +7 -1
  174. package/dist/language/flows/flow-cross-step.js +9 -0
  175. package/dist/language/flows/flow-expect-grammar.js +15 -10
  176. package/dist/language/flows/flow-intent.js +6 -1
  177. package/dist/language/flows/flow-journey.d.ts +8 -0
  178. package/dist/language/flows/flow-journey.js +13 -0
  179. package/dist/language/flows/flow-lease.d.ts +27 -0
  180. package/dist/language/flows/flow-lease.js +59 -0
  181. package/dist/language/flows/flow-replay-run.d.ts +1 -87
  182. package/dist/language/flows/flow-replay-run.js +111 -310
  183. package/dist/language/flows/flow-replay.js +21 -19
  184. package/dist/language/flows/flow-replayable.d.ts +38 -0
  185. package/dist/language/flows/flow-replayable.js +179 -0
  186. package/dist/language/flows/flow-start-path.d.ts +108 -0
  187. package/dist/language/flows/flow-start-path.js +313 -0
  188. package/dist/language/flows/flow-step-runners.d.ts +23 -2
  189. package/dist/language/flows/flow-step-runners.js +191 -32
  190. package/dist/language/flows/flow-store-for-session.d.ts +8 -1
  191. package/dist/language/flows/flow-store-for-session.js +6 -0
  192. package/dist/language/flows/flow-tools.d.ts +2 -23
  193. package/dist/language/flows/flow-tools.js +63 -113
  194. package/dist/language/flows/flows.d.ts +4 -6
  195. package/dist/language/flows/flows.js +90 -15
  196. package/dist/language/flows/heal-run.d.ts +10 -4
  197. package/dist/language/flows/heal-run.js +26 -9
  198. package/dist/language/flows/recording/flake.d.ts +3 -27
  199. package/dist/language/flows/recording/flake.js +1 -1
  200. package/dist/language/flows/recording/recording-backtrack.js +1 -4
  201. package/dist/language/flows/recording/tape/recordings.d.ts +11 -1
  202. package/dist/language/flows/recording/tape/recordings.js +9 -3
  203. package/dist/language/flows/replay.d.ts +25 -5
  204. package/dist/language/flows/replay.js +83 -16
  205. package/dist/language/flows/stores/assertion-tiers-store.js +1 -1
  206. package/dist/language/flows/suite/coverage.d.ts +9 -0
  207. package/dist/language/flows/suite/coverage.js +12 -1
  208. package/dist/language/flows/suite-selection.d.ts +25 -0
  209. package/dist/language/flows/suite-selection.js +41 -9
  210. package/dist/machine/git-tracks.d.ts +6 -0
  211. package/dist/machine/git-tracks.js +36 -0
  212. package/dist/memory/cloud/background-sync.d.ts +25 -0
  213. package/dist/memory/cloud/background-sync.js +79 -0
  214. package/dist/memory/cloud/cloud-config.d.ts +12 -1
  215. package/dist/memory/cloud/cloud-config.js +39 -13
  216. package/dist/memory/cloud/cloud-sync.d.ts +3 -24
  217. package/dist/memory/cloud/cloud-sync.js +5 -2
  218. package/dist/memory/cloud/harness-config.d.ts +0 -7
  219. package/dist/memory/cloud/memory-scope.d.ts +19 -0
  220. package/dist/memory/cloud/memory-scope.js +48 -0
  221. package/dist/memory/cloud/sync-answer.d.ts +43 -0
  222. package/dist/memory/cloud/sync-answer.js +42 -0
  223. package/dist/memory/cloud/sync-cycle.d.ts +40 -10
  224. package/dist/memory/cloud/sync-cycle.js +97 -144
  225. package/dist/memory/cloud/sync-daemon.d.ts +50 -3
  226. package/dist/memory/cloud/sync-daemon.js +473 -83
  227. package/dist/memory/cloud/sync-describe.d.ts +28 -0
  228. package/dist/memory/cloud/sync-describe.js +92 -0
  229. package/dist/memory/cloud/sync-disk.d.ts +9 -1
  230. package/dist/memory/cloud/sync-disk.js +137 -33
  231. package/dist/memory/cloud/sync-flow-versions.d.ts +26 -0
  232. package/dist/memory/cloud/sync-flow-versions.js +42 -0
  233. package/dist/memory/impact/impact-store.js +32 -15
  234. package/dist/memory/intent/intent-shard.d.ts +41 -266
  235. package/dist/memory/intent/intent-tools.js +20 -61
  236. package/dist/memory/intent/record-request.d.ts +27 -0
  237. package/dist/memory/intent/record-request.js +88 -0
  238. package/dist/memory/intent/settled-words.js +9 -1
  239. package/dist/memory/journal/drive-flow.d.ts +3 -0
  240. package/dist/memory/journal/drive-flow.js +11 -41
  241. package/dist/memory/journal/envelope-store.js +1 -1
  242. package/dist/memory/journal/envelope.d.ts +6 -154
  243. package/dist/memory/journal/episode-log.d.ts +107 -0
  244. package/dist/memory/journal/episode-log.js +302 -0
  245. package/dist/memory/journal/journal-recorder.d.ts +7 -0
  246. package/dist/memory/journal/journal-recorder.js +22 -9
  247. package/dist/memory/journal/on-disk/ambient-file.d.ts +1 -7
  248. package/dist/memory/journal/on-disk/ambient-file.js +1 -1
  249. package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +24 -3
  250. package/dist/memory/journal/on-disk/workspace-gitignore.js +38 -7
  251. package/dist/memory/journal/on-disk/workspace-tiers.js +7 -0
  252. package/dist/memory/journal/self-observation.js +3 -1
  253. package/dist/memory/journal/session-end.js +23 -9
  254. package/dist/memory/journal/session-journal.js +39 -20
  255. package/dist/memory/project/artifact-root-resolver.d.ts +14 -1
  256. package/dist/memory/project/artifact-root-resolver.js +68 -23
  257. package/dist/memory/project/artifact-root.d.ts +17 -3
  258. package/dist/memory/project/artifact-root.js +47 -9
  259. package/dist/memory/project/fs/fs-port.d.ts +5 -0
  260. package/dist/memory/project/fs/fs-port.js +13 -0
  261. package/dist/memory/project/learned-routes.d.ts +3 -4
  262. package/dist/memory/project/learned-routes.js +3 -11
  263. package/dist/memory/project/link-directory.js +5 -2
  264. package/dist/memory/project/root-claims.d.ts +3 -1
  265. package/dist/memory/project/root-claims.js +41 -7
  266. package/dist/memory/project/session-root.d.ts +1 -1
  267. package/dist/memory/project/session-root.js +1 -15
  268. package/dist/memory/project/sync-status.js +5 -6
  269. package/dist/memory/recall/memory-tools.js +2 -2
  270. package/dist/memory/recall/platform-memory.d.ts +3 -1
  271. package/dist/memory/recall/platform-memory.js +9 -1
  272. package/dist/memory/recall/project-memory.js +15 -19
  273. package/dist/portal/bridge/auth-failure-reason.d.ts +13 -0
  274. package/dist/portal/bridge/auth-failure-reason.js +19 -0
  275. package/dist/portal/bridge/bridge.d.ts +19 -4
  276. package/dist/portal/bridge/bridge.js +150 -177
  277. package/dist/portal/bridge/harness-request.d.ts +15 -0
  278. package/dist/portal/bridge/harness-request.js +19 -0
  279. package/dist/portal/bridge/origin/origin-follow.d.ts +46 -0
  280. package/dist/portal/bridge/origin/origin-follow.js +110 -0
  281. package/dist/portal/bridge/pairing-token.d.ts +6 -0
  282. package/dist/portal/bridge/pairing-token.js +47 -16
  283. package/dist/portal/bridge/protocol-skew.d.ts +27 -2
  284. package/dist/portal/bridge/protocol-skew.js +66 -2
  285. package/dist/portal/bridge/session-nonces.d.ts +46 -0
  286. package/dist/portal/bridge/session-nonces.js +72 -0
  287. package/dist/portal/input/viewport-tools.d.ts +31 -0
  288. package/dist/portal/input/viewport-tools.js +106 -15
  289. package/dist/portal/pool/bounded-close.d.ts +23 -0
  290. package/dist/portal/pool/bounded-close.js +38 -0
  291. package/dist/portal/pool/browser-pool.d.ts +44 -39
  292. package/dist/portal/pool/browser-pool.js +247 -95
  293. package/dist/portal/pool/context-permissions.d.ts +9 -1
  294. package/dist/portal/pool/context-permissions.js +26 -0
  295. package/dist/portal/pool/lease-emulation.d.ts +37 -0
  296. package/dist/portal/pool/lease-emulation.js +29 -0
  297. package/dist/portal/pool/playwright-launcher.d.ts +6 -0
  298. package/dist/portal/pool/playwright-launcher.js +74 -7
  299. package/dist/portal/pool/pool-contract.d.ts +23 -1
  300. package/dist/portal/pool/pool-lease.d.ts +128 -0
  301. package/dist/portal/pool/pool-lease.js +49 -0
  302. package/dist/portal/pool/zero-install.d.ts +8 -1
  303. package/dist/portal/pool/zero-install.js +24 -2
  304. package/dist/portal/production/production-activation.d.ts +136 -0
  305. package/dist/portal/production/production-activation.js +173 -0
  306. package/dist/portal/production/production-drive.d.ts +52 -0
  307. package/dist/portal/production/production-drive.js +301 -0
  308. package/dist/portal/production/production-hello.d.ts +23 -0
  309. package/dist/portal/production/production-hello.js +23 -0
  310. package/dist/portal/realm/web-realm.js +42 -15
  311. package/dist/portal/session/control-envelope.d.ts +1 -1
  312. package/dist/portal/session/dev-server/served-document.d.ts +0 -5
  313. package/dist/portal/session/dev-server/served-document.js +6 -8
  314. package/dist/portal/session/facts/handshake-facts.d.ts +8 -1
  315. package/dist/portal/session/first-run-wiring.d.ts +3 -36
  316. package/dist/portal/session/first-run-wiring.js +10 -103
  317. package/dist/portal/session/live-control-tools.js +61 -19
  318. package/dist/portal/session/no-session-next-action.d.ts +11 -0
  319. package/dist/portal/session/no-session-next-action.js +40 -15
  320. package/dist/portal/session/no-session-watch.d.ts +7 -0
  321. package/dist/portal/session/no-session-watch.js +67 -23
  322. package/dist/portal/session/output-budget.d.ts +10 -0
  323. package/dist/portal/session/output-budget.js +16 -0
  324. package/dist/portal/session/presence/session-lease.d.ts +2 -0
  325. package/dist/portal/session/presence/session-lease.js +8 -0
  326. package/dist/portal/session/recorded-gaps.d.ts +15 -11
  327. package/dist/portal/session/recorded-gaps.js +39 -18
  328. package/dist/portal/session/serving-directory.d.ts +24 -2
  329. package/dist/portal/session/serving-directory.js +93 -5
  330. package/dist/portal/session/session-info.d.ts +26 -0
  331. package/dist/portal/session/session-info.js +20 -0
  332. package/dist/portal/session/session-manager.d.ts +35 -3
  333. package/dist/portal/session/session-manager.js +69 -14
  334. package/dist/portal/session/session-successor.js +1 -9
  335. package/dist/portal/session/session-tools.js +22 -4
  336. package/dist/portal/session/session.d.ts +10 -10
  337. package/dist/portal/session/session.js +37 -29
  338. package/dist/portal/session/zero-install-first-call.d.ts +7 -2
  339. package/dist/portal/session/zero-install-first-call.js +43 -20
  340. package/dist/start-wiring.d.ts +2 -2
  341. package/dist/start-wiring.js +17 -3
  342. package/dist/status-payload.d.ts +11 -1
  343. package/dist/status-payload.js +7 -1
  344. package/dist/surface/agent-presence-grace.d.ts +11 -0
  345. package/dist/surface/agent-presence-grace.js +34 -0
  346. package/dist/surface/http-server.d.ts +12 -1
  347. package/dist/surface/http-server.js +64 -15
  348. package/dist/surface/mcp/faults/mcp-outage.d.ts +12 -2
  349. package/dist/surface/mcp/faults/mcp-outage.js +19 -1
  350. package/dist/surface/mcp/mcp-proxy.d.ts +17 -74
  351. package/dist/surface/mcp/mcp-proxy.js +194 -267
  352. package/dist/surface/mcp/mcp.d.ts +20 -3
  353. package/dist/surface/mcp/mcp.js +88 -25
  354. package/dist/surface/mcp/proxy/drive-calls.d.ts +44 -0
  355. package/dist/surface/mcp/proxy/drive-calls.js +114 -5
  356. package/dist/surface/mcp/proxy/proxy-boot.d.ts +11 -0
  357. package/dist/surface/mcp/proxy/proxy-boot.js +1 -0
  358. package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +6 -0
  359. package/dist/surface/mcp/proxy/proxy-daemon-probe.js +14 -1
  360. package/dist/surface/mcp/proxy/proxy-handshake.d.ts +34 -2
  361. package/dist/surface/mcp/proxy/proxy-handshake.js +76 -8
  362. package/dist/surface/mcp/server-instructions.js +35 -16
  363. package/dist/surface/mcp/shared-params.d.ts +8 -0
  364. package/dist/surface/mcp/shared-params.js +15 -1
  365. package/dist/surface/mcp/sse-frame-parser.d.ts +11 -0
  366. package/dist/surface/mcp/sse-frame-parser.js +18 -0
  367. package/dist/surface/mcp/surface-vocabulary.d.ts +2 -0
  368. package/dist/surface/mcp/surface-vocabulary.js +13 -0
  369. package/dist/surface/mcp/tool-catalog-cache.d.ts +2 -0
  370. package/dist/surface/mcp/tool-catalog-cache.js +14 -4
  371. package/dist/surface/tools/account-tool.d.ts +4 -3
  372. package/dist/surface/tools/account-tool.js +20 -31
  373. package/dist/surface/tools/act/act-danger.d.ts +2 -0
  374. package/dist/surface/tools/act/act-danger.js +41 -14
  375. package/dist/surface/tools/act/act-observation.d.ts +11 -0
  376. package/dist/surface/tools/act/act-observation.js +33 -0
  377. package/dist/surface/tools/act/act-preflight.js +10 -3
  378. package/dist/surface/tools/act/act-sequence-retry.js +2 -1
  379. package/dist/surface/tools/act/act-target.js +19 -5
  380. package/dist/surface/tools/act/durable.js +1 -1
  381. package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +9 -7
  382. package/dist/surface/tools/act/navigation/navigate-arrival.js +11 -14
  383. package/dist/surface/tools/act/unsettled-find.d.ts +36 -0
  384. package/dist/surface/tools/act/unsettled-find.js +71 -0
  385. package/dist/surface/tools/act-sequence-tool.js +53 -24
  386. package/dist/surface/tools/act-tools.d.ts +2 -3
  387. package/dist/surface/tools/act-tools.js +49 -47
  388. package/dist/surface/tools/agent/compact-envelope.d.ts +2 -2
  389. package/dist/surface/tools/agent/for-the-agent.js +41 -3
  390. package/dist/surface/tools/assert/assert-grade.js +8 -3
  391. package/dist/surface/tools/assert/assert-verdict.js +4 -2
  392. package/dist/surface/tools/browser-tools.js +7 -4
  393. package/dist/surface/tools/chat-drives.js +50 -25
  394. package/dist/surface/tools/coverage-tools.js +1 -1
  395. package/dist/surface/tools/dynamic-tools.d.ts +8 -0
  396. package/dist/surface/tools/dynamic-tools.js +89 -34
  397. package/dist/surface/tools/empty-session-list.d.ts +18 -25
  398. package/dist/surface/tools/empty-session-list.js +67 -38
  399. package/dist/surface/tools/error-recovery.d.ts +17 -7
  400. package/dist/surface/tools/error-recovery.js +53 -20
  401. package/dist/surface/tools/explore-tools.d.ts +10 -0
  402. package/dist/surface/tools/explore-tools.js +72 -19
  403. package/dist/surface/tools/feedback-tools.js +23 -2
  404. package/dist/surface/tools/gap-novelty.d.ts +6 -0
  405. package/dist/surface/tools/gap-novelty.js +9 -0
  406. package/dist/surface/tools/gaps/capability-absences.js +4 -3
  407. package/dist/surface/tools/harness-explore.d.ts +1 -1
  408. package/dist/surface/tools/harness-explore.js +6 -4
  409. package/dist/surface/tools/harness-gate.d.ts +43 -21
  410. package/dist/surface/tools/harness-gate.js +55 -81
  411. package/dist/surface/tools/harness-script.d.ts +2 -0
  412. package/dist/surface/tools/harness-script.js +11 -6
  413. package/dist/surface/tools/harness-toolset.js +3 -4
  414. package/dist/surface/tools/invoke-tool.d.ts +0 -8
  415. package/dist/surface/tools/invoke-tool.js +164 -178
  416. package/dist/surface/tools/lease-hint.d.ts +9 -0
  417. package/dist/surface/tools/lease-hint.js +22 -3
  418. package/dist/surface/tools/lease-permissions.d.ts +1 -1
  419. package/dist/surface/tools/lease-readiness.d.ts +55 -0
  420. package/dist/surface/tools/lease-readiness.js +150 -2
  421. package/dist/surface/tools/lease-tools.d.ts +3 -75
  422. package/dist/surface/tools/lease-tools.js +124 -188
  423. package/dist/surface/tools/map-tools.d.ts +20 -0
  424. package/dist/surface/tools/map-tools.js +145 -0
  425. package/dist/surface/tools/merge-tools.js +11 -7
  426. package/dist/surface/tools/next-step.js +6 -3
  427. package/dist/surface/tools/observe-tools.js +46 -35
  428. package/dist/surface/tools/platform-moment.d.ts +3 -12
  429. package/dist/surface/tools/platform-moment.js +12 -24
  430. package/dist/surface/tools/read/complete-snapshot.d.ts +7 -1
  431. package/dist/surface/tools/read/complete-snapshot.js +13 -2
  432. package/dist/surface/tools/read/query-paginate.js +6 -0
  433. package/dist/surface/tools/read/query-shape.d.ts +9 -0
  434. package/dist/surface/tools/read/query-shape.js +15 -0
  435. package/dist/surface/tools/read/snapshot-unmounted-note.d.ts +1 -0
  436. package/dist/surface/tools/read/snapshot-unmounted-note.js +73 -0
  437. package/dist/surface/tools/read-tools.js +5 -5
  438. package/dist/surface/tools/real-input-attempt.d.ts +11 -0
  439. package/dist/surface/tools/real-input-attempt.js +206 -38
  440. package/dist/surface/tools/reconcile-tools.js +21 -8
  441. package/dist/surface/tools/reticle-result.d.ts +3 -0
  442. package/dist/surface/tools/reticle-result.js +8 -1
  443. package/dist/surface/tools/run-report.d.ts +22 -0
  444. package/dist/surface/tools/run-report.js +141 -0
  445. package/dist/surface/tools/session-ports.d.ts +57 -0
  446. package/dist/surface/tools/session-ports.js +102 -0
  447. package/dist/surface/tools/tool-kit.d.ts +58 -4
  448. package/dist/surface/tools/tool-kit.js +140 -6
  449. package/dist/surface/tools/tool-surface.d.ts +2 -0
  450. package/dist/surface/tools/tool-surface.js +45 -0
  451. package/dist/surface/tools/tools.js +49 -87
  452. package/dist/telemetry/automation-hint.d.ts +5 -0
  453. package/dist/telemetry/automation-hint.js +7 -0
  454. package/dist/telemetry/cli-telemetry.d.ts +22 -0
  455. package/dist/telemetry/cli-telemetry.js +80 -11
  456. package/dist/telemetry/daemon-telemetry.d.ts +3 -1
  457. package/dist/telemetry/daemon-telemetry.js +9 -2
  458. package/dist/telemetry/feedback-cli.d.ts +1 -1
  459. package/dist/telemetry/feedback-cli.js +8 -3
  460. package/dist/telemetry/feedback.d.ts +1 -1
  461. package/dist/telemetry/feedback.js +51 -4
  462. package/dist/telemetry/harness-run.d.ts +3 -3
  463. package/dist/telemetry/harness-run.js +18 -7
  464. package/dist/telemetry/hud-events.d.ts +1 -1
  465. package/dist/telemetry/hud-metrics.d.ts +6 -3
  466. package/dist/telemetry/hud-metrics.js +20 -8
  467. package/dist/telemetry/install-source.js +1 -1
  468. package/dist/telemetry/mcp-connection.d.ts +1 -1
  469. package/dist/telemetry/mcp-connection.js +21 -2
  470. package/dist/telemetry/onboarding-firsts.d.ts +6 -1
  471. package/dist/telemetry/onboarding-firsts.js +41 -1
  472. package/dist/telemetry/proxy-telemetry.d.ts +42 -0
  473. package/dist/telemetry/proxy-telemetry.js +70 -1
  474. package/dist/telemetry/session-metrics.d.ts +1 -3
  475. package/dist/telemetry/session-metrics.js +16 -12
  476. package/dist/telemetry/telemetry.d.ts +9 -1
  477. package/dist/telemetry/telemetry.js +16 -1
  478. package/dist/telemetry/verification-of.d.ts +3 -1
  479. package/dist/telemetry/verification-of.js +7 -1
  480. package/dist/wire-journal.js +12 -2
  481. package/docs/README.md +4 -2
  482. package/docs/actions.mdx +7 -6
  483. package/docs/adapters.md +2 -1
  484. package/docs/agent-cheatsheet.md +19 -5
  485. package/docs/architecture.md +13 -10
  486. package/docs/autodrive.md +6 -5
  487. package/docs/benchmarks.md +2 -2
  488. package/docs/capabilities.mdx +1 -1
  489. package/docs/cli/affected.mdx +1 -1
  490. package/docs/cli/cloud.mdx +6 -6
  491. package/docs/cli/doctor.mdx +3 -1
  492. package/docs/cli/feedback.mdx +11 -7
  493. package/docs/cli/gate.mdx +1 -1
  494. package/docs/cli/help.mdx +1 -1
  495. package/docs/cli/init.mdx +20 -16
  496. package/docs/cli/mcp.mdx +5 -2
  497. package/docs/cli/open.mdx +5 -2
  498. package/docs/cli/report.mdx +1 -1
  499. package/docs/cli/restart.mdx +1 -1
  500. package/docs/cli/rollback.mdx +4 -4
  501. package/docs/cli/serve.mdx +7 -5
  502. package/docs/cli/setup.mdx +1 -1
  503. package/docs/cli/status.mdx +6 -2
  504. package/docs/cli/stop.mdx +3 -3
  505. package/docs/cli/telemetry.mdx +5 -1
  506. package/docs/cli/try.mdx +8 -8
  507. package/docs/cli/update.mdx +8 -5
  508. package/docs/cli/verify.mdx +12 -5
  509. package/docs/cli/version.mdx +1 -1
  510. package/docs/cli.mdx +10 -6
  511. package/docs/debugging.md +1 -1
  512. package/docs/deploy-checks.md +8 -6
  513. package/docs/desktop.mdx +14 -18
  514. package/docs/enterprise.md +6 -6
  515. package/docs/environment.mdx +5 -3
  516. package/docs/faq.mdx +9 -6
  517. package/docs/first-drive.md +19 -9
  518. package/docs/flows.md +3 -4
  519. package/docs/for-agents.md +1 -1
  520. package/docs/frameworks.mdx +34 -11
  521. package/docs/getting-started.md +46 -29
  522. package/docs/http-transport.md +2 -2
  523. package/docs/human-control.md +8 -8
  524. package/docs/index.mdx +2 -2
  525. package/docs/install-agentic.mdx +18 -19
  526. package/docs/install-manual.mdx +8 -4
  527. package/docs/integration-patterns.md +5 -5
  528. package/docs/multi-agent-testing.md +7 -5
  529. package/docs/packages/browser.mdx +34 -2
  530. package/docs/packages/core.mdx +2 -1
  531. package/docs/packages/electron.mdx +15 -3
  532. package/docs/packages/eslint-plugin.mdx +1 -1
  533. package/docs/packages/next.mdx +17 -5
  534. package/docs/packages/react.mdx +8 -1
  535. package/docs/packages/server.mdx +3 -3
  536. package/docs/packages/tauri.mdx +3 -3
  537. package/docs/packages/vite-plugin.mdx +1 -1
  538. package/docs/packages.mdx +19 -6
  539. package/docs/platform-integration.md +12 -4
  540. package/docs/predicates.mdx +18 -7
  541. package/docs/production-mode.md +108 -0
  542. package/docs/prompt-capture.md +99 -0
  543. package/docs/quickstart.mdx +14 -12
  544. package/docs/recipes.mdx +9 -8
  545. package/docs/required-checks.md +3 -2
  546. package/docs/skill-file.mdx +4 -3
  547. package/docs/skill-setup.md +2 -2
  548. package/docs/skill.md +62 -70
  549. package/docs/state-files.md +1 -0
  550. package/docs/state-management.mdx +11 -9
  551. package/docs/system-map.md +14 -13
  552. package/docs/telemetry.md +32 -24
  553. package/docs/testing.md +9 -5
  554. package/docs/tools/act-and-wait.mdx +17 -13
  555. package/docs/tools/act-sequence.mdx +40 -66
  556. package/docs/tools/act.mdx +7 -5
  557. package/docs/tools/assert.mdx +1 -1
  558. package/docs/tools/look.mdx +1 -1
  559. package/docs/tools/navigate.mdx +1 -1
  560. package/docs/tools/network.mdx +13 -11
  561. package/docs/tools/overview.mdx +3 -3
  562. package/docs/tools/query.mdx +3 -3
  563. package/docs/tools/session-and-feedback.mdx +9 -6
  564. package/docs/tools/sessions.mdx +7 -0
  565. package/docs/tools/snapshot.mdx +9 -7
  566. package/docs/tools/tools-and-run.mdx +11 -10
  567. package/docs/tools/verify.mdx +1 -1
  568. package/docs/troubleshooting.mdx +21 -9
  569. package/docs/usage.md +58 -43
  570. package/docs/use-cases.mdx +2 -2
  571. package/docs/vs-chrome-devtools-mcp.mdx +5 -5
  572. package/docs/vs-playwright-mcp.mdx +5 -4
  573. package/docs/what-is-recorded.md +12 -7
  574. package/docs/what-is-reticle.mdx +2 -2
  575. package/docs/why-reticle.mdx +1 -1
  576. package/package.json +9 -12
  577. package/dist/command/dev/stale-issue-guard.d.ts +0 -34
  578. package/dist/command/dev/stale-issue-guard.js +0 -69
@@ -24,19 +24,26 @@ import { PROJECT_REGISTRY_FILE, emptyProjectRegistry, parseProjectRegistry, } fr
24
24
  import { DevServerEntrySchema, ReticleDir, ReticleEnv, devServerRegistryPort, } from '@reticlehq/core';
25
25
  import { discoverProjectConfigs, hasProjectConfig, } from '../../command/cli/config/config-discovery.js';
26
26
  import { log } from '../../log.js';
27
- import { ArtifactRootReason, projectCandidatesFrom, resolveArtifactRoot, unmatchedRoot, } from './artifact-root.js';
27
+ import { isUnmatchedRoot } from './dir/reticle-dir.js';
28
+ import { checkoutAt, hasLinkedWorktrees } from '../../command/cli/config/checkout.js';
29
+ import { ArtifactRootReason, projectCandidatesFrom, rebaseIntoCheckout, resolveArtifactRoot, unmatchedRoot, } from './artifact-root.js';
28
30
  /**
29
- * Is the daemon's own directory a Reticle project, or is it a guest there?
31
+ * May the daemon write into this `.reticle` root? This is the one answer for every writer.
30
32
  *
31
- * Answered ONCE per resolver rather than per session: a directory does not become a project between
32
- * two tabs connecting, and this is the IO half `unmatchedRoot` deliberately does not do.
33
+ * The resolver asks it ONCE for the daemon's own root: a directory does not become a project
34
+ * between two tabs connecting, and this is the IO half `unmatchedRoot` deliberately does not do.
35
+ * A writer that holds a root for the daemon's life (the impact ledger) asks it on each write, so
36
+ * that it starts to write into the project when `init` wires it.
33
37
  */
34
- function daemonSitsInAProject(daemonRoot) {
38
+ export function mayWriteIntoRoot(reticleRoot) {
35
39
  try {
36
40
  // Either mark counts. `.reticle.json` means somebody ran `init` here; an existing `.reticle/`
37
41
  // means Reticle has been invited into this tree before, including by a version that predates
38
42
  // the config file. A directory with neither has never agreed to hold anybody's session data.
39
- return existsSync(daemonRoot) || hasProjectConfig(dirname(daemonRoot));
43
+ // An unmatched bucket is in the user's own `~/.reticle`, which is Reticle's to write.
44
+ return (existsSync(reticleRoot) ||
45
+ hasProjectConfig(dirname(reticleRoot)) ||
46
+ isUnmatchedRoot(reticleRoot));
40
47
  }
41
48
  catch {
42
49
  // Unreadable is not a licence to write. Treat it as somebody else's directory.
@@ -46,7 +53,7 @@ function daemonSitsInAProject(daemonRoot) {
46
53
  /**
47
54
  * The project a candidate list says lives at the daemon's own directory.
48
55
  *
49
- * Not the same question as `daemonSitsInAProject`: that one asks whether the tree invited Reticle in
56
+ * Not the same question as `mayWriteIntoRoot`: that one asks whether the tree invited Reticle in
50
57
  * at all (either mark counts, including a bare `.reticle/` from a version that predates the config
51
58
  * file), and this one asks WHICH project it is. The difference is the whole of #1244 — a daemon in
52
59
  * repo A serving an app from a git worktree B belongs to A and is being asked about B.
@@ -115,12 +122,14 @@ function announcedDevServers() {
115
122
  const servers = [];
116
123
  try {
117
124
  for (const name of readdirSync(dir)) {
118
- if (null === devServerRegistryPort(name))
125
+ if (null === devServerRegistryPort(name)) {
119
126
  continue;
127
+ }
120
128
  try {
121
129
  const parsed = DevServerEntrySchema.safeParse(JSON.parse(readFileSync(join(dir, name), 'utf8')));
122
- if (parsed.success)
130
+ if (parsed.success) {
123
131
  servers.push({ projectId: parsed.data.projectId, root: parsed.data.root });
132
+ }
124
133
  }
125
134
  catch {
126
135
  /* one bad announcement does not hide the others */
@@ -142,8 +151,9 @@ function announcedDevServers() {
142
151
  * own project id.
143
152
  */
144
153
  export function projectDirectoryFor(projectId) {
145
- if (projectId === undefined)
154
+ if (projectId === undefined) {
146
155
  return undefined;
156
+ }
147
157
  const directories = new Set(knownProjectCandidates()
148
158
  .filter((candidate) => candidate.projectId === projectId)
149
159
  .map((candidate) => candidate.directory));
@@ -153,7 +163,7 @@ export function projectDirectoryFor(projectId) {
153
163
  }
154
164
  export function artifactRootResolver(daemonRoot, deps) {
155
165
  const candidates = deps.candidates ?? knownProjectCandidates;
156
- const sitsInAProject = deps.daemonIsProject ?? daemonSitsInAProject;
166
+ const sitsInAProject = deps.daemonIsProject ?? mayWriteIntoRoot;
157
167
  const { servingDirectory } = deps;
158
168
  const daemonIsProject = sitsInAProject(daemonRoot);
159
169
  // Resolved beside it and ONCE, for the same reason: a directory's `.reticle.json` does not change
@@ -164,15 +174,29 @@ export function artifactRootResolver(daemonRoot, deps) {
164
174
  : undefined;
165
175
  /** Said once per project, reason and folder: one session's calls repeated it 16 times in 3s. */
166
176
  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);
177
+ const checkout = deps.checkout ?? checkoutAt;
178
+ const hasWorktrees = deps.hasWorktrees ?? hasLinkedWorktrees;
179
+ /**
180
+ * The one folder `directories` stand for in the checkout that served the page, or undefined when
181
+ * they do not agree. Every worktree of a project declares the same id, and discovery usually knows
182
+ * only some of them, so the page's own checkout decides and a worktree's work stays on its branch.
183
+ */
184
+ const inServingCheckout = (directories, serving) => {
185
+ const to = serving === undefined ? undefined : checkout(serving);
186
+ const moved = new Set(directories.map((d) => rebaseIntoCheckout(d, checkout(d), to)));
187
+ return 1 === moved.size ? [...moved][0] : undefined;
174
188
  };
175
189
  return (projectId, origin) => {
190
+ // Asked at most once for each connection, and again for the next one: it can shell out, and a
191
+ // port that moves to another worktree is a new connection whose work must move with it.
192
+ let served;
193
+ const serving = () => {
194
+ if (origin === undefined) {
195
+ return undefined;
196
+ }
197
+ served ??= { directory: servingDirectory(origin) };
198
+ return served.directory;
199
+ };
176
200
  const known = candidates();
177
201
  let resolved = resolveArtifactRoot({ projectId, candidates: known, daemonRoot });
178
202
  // Several checkouts declare this project — every git worktree carries the same committed
@@ -183,11 +207,32 @@ export function artifactRootResolver(daemonRoot, deps) {
183
207
  projectId,
184
208
  candidates: known,
185
209
  daemonRoot,
186
- servingDirectory: servingOf(origin),
210
+ servingDirectory: serving(),
187
211
  });
212
+ // Worktrees nested in the main checkout (`.claude/worktrees/a`) are all candidates, and a page
213
+ // served from another worktree is inside none of them. It fell back to the daemon's folder.
214
+ const directory = resolved.reason === ArtifactRootReason.AMBIGUOUS
215
+ ? inServingCheckout(resolved.candidates ?? [], serving())
216
+ : undefined;
217
+ if (directory !== undefined) {
218
+ resolved = {
219
+ root: join(directory, ReticleDir.ROOT),
220
+ reason: ArtifactRootReason.MATCHED_PROJECT,
221
+ };
222
+ }
223
+ }
224
+ if (resolved.reason === ArtifactRootReason.MATCHED_PROJECT) {
225
+ const project = dirname(resolved.root);
226
+ const from = origin === undefined ? undefined : checkout(project);
227
+ // Only a repository that HAS worktrees pays for the serving lookup.
228
+ if (from === undefined || !hasWorktrees(from.commonDir)) {
229
+ return resolved;
230
+ }
231
+ const moved = inServingCheckout([project], serving());
232
+ return moved === undefined || moved === project
233
+ ? resolved
234
+ : { ...resolved, root: join(moved, ReticleDir.ROOT) };
188
235
  }
189
- if (resolved.reason === ArtifactRootReason.MATCHED_PROJECT)
190
- return resolved;
191
236
  // Could not name the project. The old code wrote into the daemon's directory anyway and said
192
237
  // nothing, which put `.reticle/` — journals included — into repositories nobody had
193
238
  // instrumented. Evidence still has to live somewhere, so it goes to the user's own
@@ -219,7 +264,7 @@ export function artifactRootResolver(daemonRoot, deps) {
219
264
  }
220
265
  // Named so the agent is told WHICH checkout's work is stranded: a clone serving the app was
221
266
  // never named, only the primary checkout's folders.
222
- const serving = origin === undefined ? undefined : servingOf(origin);
223
- return { ...resolved, root, ...(serving === undefined ? {} : { serving }) };
267
+ const servedFrom = serving();
268
+ return { ...resolved, root, ...(servedFrom === undefined ? {} : { serving: servedFrom }) };
224
269
  };
225
270
  }
@@ -1,5 +1,5 @@
1
1
  import { UNMATCHED_SUBDIR } from './dir/reticle-dir.js';
2
- import { type ProjectCandidate, type ProjectId, type ProjectRegistry } from '@reticlehq/core';
2
+ import { type Checkout, type ProjectCandidate, type ProjectId, type ProjectRegistry } from '@reticlehq/core';
3
3
  import type { ConfigDiscovery } from '../../command/cli/config/config-discovery.js';
4
4
  /**
5
5
  * Which `.reticle/` a session's artifacts belong in.
@@ -46,11 +46,16 @@ interface ArtifactRootQuery {
46
46
  daemonRoot: string;
47
47
  /**
48
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
49
+ * Consulted to break an AMBIGUOUS tie: every git worktree carries the same committed
50
50
  * `.reticle.json`, so the projectId cannot tell them apart, but the serving process runs inside
51
- * exactly one of them.
51
+ * exactly one of them. A lone match in a different tree from it is a second checkout, refused.
52
52
  */
53
53
  servingDirectory?: string | undefined;
54
+ /**
55
+ * The platform whose path rules apply. Windows paths are not case-sensitive, so `C:\app` and
56
+ * `c:\app` are one folder there. Defaults to this process's platform; a test names it.
57
+ */
58
+ platform?: NodeJS.Platform;
54
59
  }
55
60
  export interface ArtifactRoot {
56
61
  /** Absolute path to the `.reticle` directory. Always present, on every branch. */
@@ -71,6 +76,15 @@ export interface ArtifactRoot {
71
76
  * passed in, which is what makes every branch below testable without a fixture tree.
72
77
  */
73
78
  export declare function resolveArtifactRoot(query: ArtifactRootQuery): ArtifactRoot;
79
+ /**
80
+ * The same project folder, in the checkout that served the page.
81
+ *
82
+ * Every git worktree of a project is the same project, so the config the daemon found names the
83
+ * main checkout's folder. A page served from worktree `x` belongs in `x`'s copy of that folder, so
84
+ * its flows and runs travel with `x`'s branch. Unchanged unless both checkouts are known, share one
85
+ * main checkout, and differ.
86
+ */
87
+ export declare function rebaseIntoCheckout(directory: string, from: Pick<Checkout, 'root' | 'commonDir'> | undefined, to: Pick<Checkout, 'root' | 'commonDir'> | undefined): string;
74
88
  /**
75
89
  * Everything this machine knows about where projects live, from both sources.
76
90
  *
@@ -1,6 +1,6 @@
1
1
  import { isAbsolute, join, relative, sep } from 'node:path';
2
2
  import { UNMATCHED_SUBDIR } from './dir/reticle-dir.js';
3
- import { SAFE_SEGMENT_PATTERN, ReticleDir, fnv1a, projectCandidates, } from '@reticlehq/core';
3
+ import { SAFE_SEGMENT_PATTERN, ReticleDir, fnv1a, projectCandidates, samePath, } from '@reticlehq/core';
4
4
  /**
5
5
  * Which `.reticle/` a session's artifacts belong in.
6
6
  *
@@ -47,7 +47,7 @@ export function resolveArtifactRoot(query) {
47
47
  if (projectId === undefined || 0 === projectId.length) {
48
48
  return { root: daemonRoot, reason: ArtifactRootReason.NO_PROJECT_ID };
49
49
  }
50
- const matches = dedupeByDirectory(candidates.filter((c) => c.projectId === projectId));
50
+ const matches = dedupeByDirectory(candidates.filter((c) => c.projectId === projectId), query.platform ?? process.platform);
51
51
  if (0 === matches.length) {
52
52
  return { root: daemonRoot, reason: ArtifactRootReason.NO_MATCH };
53
53
  }
@@ -69,8 +69,40 @@ export function resolveArtifactRoot(query) {
69
69
  if (0 === directory.length) {
70
70
  return { root: daemonRoot, reason: ArtifactRootReason.NO_MATCH };
71
71
  }
72
+ // The page was served from a tree apart from the one checkout we know: a second checkout of the
73
+ // same project that the registry no longer names. Writing into the known one moves this app's
74
+ // evidence into a tree nobody drove, so it is refused like any other pair.
75
+ const serving = query.servingDirectory;
76
+ if (serving !== undefined &&
77
+ 0 < serving.length &&
78
+ !isSameOrInside(directory, serving) &&
79
+ !isSameOrInside(serving, directory)) {
80
+ return {
81
+ root: daemonRoot,
82
+ reason: ArtifactRootReason.AMBIGUOUS,
83
+ candidates: [directory, serving],
84
+ };
85
+ }
72
86
  return { root: join(directory, ReticleDir.ROOT), reason: ArtifactRootReason.MATCHED_PROJECT };
73
87
  }
88
+ /**
89
+ * The same project folder, in the checkout that served the page.
90
+ *
91
+ * Every git worktree of a project is the same project, so the config the daemon found names the
92
+ * main checkout's folder. A page served from worktree `x` belongs in `x`'s copy of that folder, so
93
+ * its flows and runs travel with `x`'s branch. Unchanged unless both checkouts are known, share one
94
+ * main checkout, and differ.
95
+ */
96
+ export function rebaseIntoCheckout(directory, from, to) {
97
+ if (from === undefined || to === undefined)
98
+ return directory;
99
+ if (!samePath(from.commonDir, to.commonDir) || samePath(from.root, to.root))
100
+ return directory;
101
+ const rel = relative(from.root, directory);
102
+ if ('..' === rel || rel.startsWith(`..${sep}`) || isAbsolute(rel))
103
+ return directory;
104
+ return join(to.root, rel);
105
+ }
74
106
  /**
75
107
  * The checkout the serving directory sits in, or undefined when it is in none of them.
76
108
  *
@@ -83,12 +115,14 @@ function checkoutContaining(matches, servingDirectory) {
83
115
  return undefined;
84
116
  const inside = matches
85
117
  .map((candidate) => candidate.directory)
86
- .filter((directory) => {
87
- const rel = relative(directory, servingDirectory);
88
- return '' === rel || ('..' !== rel && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
89
- });
118
+ .filter((directory) => isSameOrInside(directory, servingDirectory));
90
119
  return inside.sort((a, b) => b.length - a.length)[0];
91
120
  }
121
+ /** Is `path` the directory `dir` or somewhere inside it? */
122
+ function isSameOrInside(dir, path) {
123
+ const rel = relative(dir, path);
124
+ return '' === rel || ('..' !== rel && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
125
+ }
92
126
  /**
93
127
  * One entry per directory.
94
128
  *
@@ -97,13 +131,17 @@ function checkoutContaining(matches, servingDirectory) {
97
131
  * it twice would read as two competing checkouts and make the resolver refuse, which is the one
98
132
  * outcome worse than either source alone.
99
133
  */
100
- function dedupeByDirectory(matches) {
134
+ function dedupeByDirectory(matches, platform) {
135
+ // Windows paths are not case-sensitive: one checkout seen from two shells can differ by case.
136
+ const keyOf = (directory) => 'win32' === platform ? directory.toLowerCase() : directory;
101
137
  const seen = new Set();
102
138
  const out = [];
103
139
  for (const candidate of matches) {
104
- if (seen.has(candidate.directory))
140
+ const key = keyOf(candidate.directory);
141
+ if (seen.has(key)) {
105
142
  continue;
106
- seen.add(candidate.directory);
143
+ }
144
+ seen.add(key);
107
145
  out.push(candidate);
108
146
  }
109
147
  return out;
@@ -66,3 +66,8 @@ export interface FileSystemPort {
66
66
  }
67
67
  /** Production adapter — the single import site of node:fs/promises in the server. */
68
68
  export declare function createNodeFileSystem(): FileSystemPort;
69
+ /**
70
+ * A file's text, or undefined when it is missing, a directory or unreadable. Synchronous, for the
71
+ * few questions asked before any port exists, such as which git checkout a folder is in.
72
+ */
73
+ export declare function readTextOrUndefined(path: string): string | undefined;
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from 'node:fs';
1
2
  import { access, appendFile, mkdir, open, readdir, readFile, realpath, rename, rm, stat, writeFile, } from 'node:fs/promises';
2
3
  /** The JSONL record terminator, as a byte — the only boundary a capped tail read can align to. */
3
4
  const NEWLINE_BYTE = 0x0a;
@@ -119,3 +120,15 @@ export function createNodeFileSystem() {
119
120
  isNotFound: (error) => 'ENOENT' === error?.code,
120
121
  };
121
122
  }
123
+ /**
124
+ * A file's text, or undefined when it is missing, a directory or unreadable. Synchronous, for the
125
+ * few questions asked before any port exists, such as which git checkout a folder is in.
126
+ */
127
+ export function readTextOrUndefined(path) {
128
+ try {
129
+ return readFileSync(path, 'utf8');
130
+ }
131
+ catch {
132
+ return undefined;
133
+ }
134
+ }
@@ -1,4 +1,4 @@
1
- import { type ReticleEvent } from '@reticlehq/core';
1
+ import { pathOf as routeFromUrl, type ReticleEvent } from '@reticlehq/core';
2
2
  interface RouteLearningSession {
3
3
  url: string;
4
4
  /**
@@ -21,8 +21,8 @@ interface RouteLearningStore {
21
21
  export declare const ROUTE_PERSIST_DEBOUNCE_MS = 50;
22
22
  /** Convert a validated route-change payload into a bounded route identity. */
23
23
  export declare function routeFromEvent(event: ReticleEvent): string | undefined;
24
- /** Read the same route shape from a session's absolute URL. */
25
- export declare function routeFromUrl(value: string): string | undefined;
24
+ /** Read the same route shape from a session's absolute URL: core's `pathOf`, re-exported. */
25
+ export { routeFromUrl };
26
26
  export declare function routesFromEvents(events: readonly ReticleEvent[]): string[];
27
27
  /**
28
28
  * Persist initial and subsequent routes for existing and future browser sessions.
@@ -35,4 +35,3 @@ export declare function routesFromEvents(events: readonly ReticleEvent[]): strin
35
35
  * while the driven project's own memory never filled.
36
36
  */
37
37
  export declare function attachRouteLearning(bridge: RouteLearningBridge, storeFor: (root: string | undefined) => RouteLearningStore): void;
38
- export {};
@@ -1,4 +1,4 @@
1
- import { EventType } from '@reticlehq/core';
1
+ import { EventType, pathOf as routeFromUrl } from '@reticlehq/core';
2
2
  /** Coalesce navigation bursts so one crawl does not lock and rewrite project.json per event. */
3
3
  export const ROUTE_PERSIST_DEBOUNCE_MS = 50;
4
4
  /** Convert a validated route-change payload into a bounded route identity. */
@@ -10,16 +10,8 @@ export function routeFromEvent(event) {
10
10
  return undefined;
11
11
  return pathname;
12
12
  }
13
- /** Read the same route shape from a session's absolute URL. */
14
- export function routeFromUrl(value) {
15
- try {
16
- const url = new URL(value);
17
- return url.pathname;
18
- }
19
- catch {
20
- return undefined;
21
- }
22
- }
13
+ /** Read the same route shape from a session's absolute URL: core's `pathOf`, re-exported. */
14
+ export { routeFromUrl };
23
15
  export function routesFromEvents(events) {
24
16
  return events.flatMap((event) => {
25
17
  const route = routeFromEvent(event);
@@ -6,7 +6,7 @@
6
6
  * while every run of that app landed in it.
7
7
  */
8
8
  import { existsSync } from 'node:fs';
9
- import { join, resolve, sep } from 'node:path';
9
+ import { basename, dirname, join, resolve, sep } from 'node:path';
10
10
  import { ReticleDir } from '@reticlehq/core';
11
11
  import { discoverProjectConfigs, hasProjectConfig, } from '../../command/cli/config/config-discovery.js';
12
12
  /** `inner` is `outer` or sits inside it. */
@@ -17,7 +17,10 @@ const within = (inner, outer) => inner === outer || inner.startsWith(outer.endsW
17
17
  * kept rather than one picked silently.
18
18
  */
19
19
  export function linkDirectoryFor(cwd, discovery) {
20
- const here = resolve(cwd);
20
+ // A `.reticle` is a project's, never a project: the HUD's Link passes the tab's `.reticle`, and on
21
+ // an app with no `.reticle.json` the link landed in `.reticle/.reticle/cloud.json`.
22
+ const asked = resolve(cwd);
23
+ const here = ReticleDir.ROOT === basename(asked) ? dirname(asked) : asked;
21
24
  const enclosing = discovery.found
22
25
  .map((found) => found.directory)
23
26
  .filter((directory) => within(here, directory))
@@ -1,5 +1,7 @@
1
- /** Claim `root` for the session a lease registers. Refuses anything but an absolute `.reticle`. */
1
+ /** Claim `root` for the session a lease registers. Takes a `.reticle` folder or the project folder holding one. */
2
2
  export declare function claimArtifactRoot(leaseId: string, root: string): void;
3
+ /** Every root a lease claimed in this process, released or not. Sync reads it to find the work. */
4
+ export declare function claimedArtifactRoots(): string[];
3
5
  /** The root claimed for this session, by its id or by the lease marker on its url. */
4
6
  export declare function claimedArtifactRoot(sessionId: string, url: string | undefined): string | undefined;
5
7
  /** Forget a lease's claim, once the lease is released. */
@@ -9,22 +9,56 @@
9
9
  * reconnect after a navigation, or an app that names its own session, still finds the claim. Lives
10
10
  * until the lease is released.
11
11
  */
12
- import { basename, isAbsolute } from 'node:path';
12
+ import { statSync } from 'node:fs';
13
+ import { basename, isAbsolute, join } from 'node:path';
13
14
  import { RETICLE_URL_PARAM, ReticleDir } from '@reticlehq/core';
14
15
  const claims = new Map();
15
- /** Claim `root` for the session a lease registers. Refuses anything but an absolute `.reticle`. */
16
- export function claimArtifactRoot(leaseId, root) {
17
- if (!isAbsolute(root) || ReticleDir.ROOT !== basename(root))
16
+ /** Every root claimed in this process. Kept after release, so sync still sends what the lease wrote. */
17
+ const everClaimed = new Set();
18
+ /**
19
+ * The `.reticle` folder a caller meant by `root`. Agents pass the project folder, the one they work
20
+ * in, so a folder that holds a `.reticle` stands for it. Anything else is refused with the path to pass.
21
+ */
22
+ function resolveArtifactRoot(root) {
23
+ if (!isAbsolute(root)) {
18
24
  throw new Error(`root must be an absolute path to a ${ReticleDir.ROOT} folder, got ${root}`);
19
- claims.set(leaseId, root);
25
+ }
26
+ if (ReticleDir.ROOT === basename(root)) {
27
+ return root;
28
+ }
29
+ const inside = join(root, ReticleDir.ROOT);
30
+ if (isDirectory(inside)) {
31
+ return inside;
32
+ }
33
+ throw new Error(`root must be the ${ReticleDir.ROOT} folder, or a project folder that contains one, got ${root}. Pass ${inside}`);
34
+ }
35
+ function isDirectory(path) {
36
+ try {
37
+ return statSync(path).isDirectory();
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ /** Claim `root` for the session a lease registers. Takes a `.reticle` folder or the project folder holding one. */
44
+ export function claimArtifactRoot(leaseId, root) {
45
+ const resolved = resolveArtifactRoot(root);
46
+ claims.set(leaseId, resolved);
47
+ everClaimed.add(resolved);
48
+ }
49
+ /** Every root a lease claimed in this process, released or not. Sync reads it to find the work. */
50
+ export function claimedArtifactRoots() {
51
+ return [...everClaimed];
20
52
  }
21
53
  /** The root claimed for this session, by its id or by the lease marker on its url. */
22
54
  export function claimedArtifactRoot(sessionId, url) {
23
55
  const direct = claims.get(sessionId);
24
- if (direct !== undefined)
56
+ if (direct !== undefined) {
25
57
  return direct;
26
- if (url === undefined)
58
+ }
59
+ if (url === undefined) {
27
60
  return undefined;
61
+ }
28
62
  try {
29
63
  const lease = new URL(url).searchParams.get(RETICLE_URL_PARAM.SESSION);
30
64
  return null === lease ? undefined : claims.get(lease);
@@ -1,4 +1,4 @@
1
- import type { ProjectId } from '@reticlehq/core';
1
+ import { type ProjectId } from '@reticlehq/core';
2
2
  import type { ToolDeps } from '../../surface/tools/tool-kit.js';
3
3
  /**
4
4
  * Which project a call is about, and the page origin it came through.
@@ -1,20 +1,6 @@
1
1
  import { dirname } from 'node:path';
2
+ import { originOf } from '@reticlehq/core';
2
3
  import { claimedArtifactRoot } from './root-claims.js';
3
- /**
4
- * The scheme://host:port a session's page was served from. Local rather than the session manager's
5
- * copy because `memory/project` does not reach `portal/session`, and five lines are cheaper than a
6
- * new dependency between the two.
7
- */
8
- function originOf(url) {
9
- if (url === undefined)
10
- return undefined;
11
- try {
12
- return new URL(url).origin;
13
- }
14
- catch {
15
- return undefined;
16
- }
17
- }
18
4
  /** The target for a caller that genuinely holds no session -- the id is all there is. */
19
5
  export function projectOnly(projectId) {
20
6
  return { projectId };
@@ -14,7 +14,7 @@ import { diskSource, readCloudState } from '../cloud/sync-disk.js';
14
14
  import { hashPayload } from '../cloud/sync-hash.js';
15
15
  import { readFileSync } from 'node:fs';
16
16
  import { join } from 'node:path';
17
- import { ReticleDir } from '@reticlehq/core';
17
+ import { CONNECT_COMMAND, ReticleDir } from '@reticlehq/core';
18
18
  import { resolveProjectCloud } from '../cloud/cloud-config.js';
19
19
  import { unmatchedRoots, unsyncedRoots } from '../cloud/unsynced-roots.js';
20
20
  import { knownProjectCandidates } from './artifact-root-resolver.js';
@@ -189,7 +189,7 @@ export function overallStatus(summary) {
189
189
  /** The summary in one sentence an agent can repeat to a person. */
190
190
  export function describeSync(summary, now) {
191
191
  if (!summary.linked)
192
- return `${String(summary.runs)} run(s) exist only on this machine; nothing is synced until the project is linked (npx @reticlehq/server connect).`;
192
+ return `${String(summary.runs)} run(s) exist only on this machine; nothing is synced until the project is linked (${CONNECT_COMMAND}).`;
193
193
  const ago = summary.lastPushAt === undefined
194
194
  ? 'nothing pushed yet'
195
195
  : `last push ${String(Math.max(0, Math.round((now - summary.lastPushAt) / 1000)))}s ago`;
@@ -217,8 +217,7 @@ export async function machineUnsyncedRoots(fs, home, env) {
217
217
  ...unmatchedRoots(home),
218
218
  ];
219
219
  const cloudOf = (root) => resolveProjectCloud(fs, root, home, env);
220
- return unsyncedRoots(roots, async (root) => {
221
- const cloud = await cloudOf(root);
222
- return null !== cloud.config && null !== cloud.projectId;
223
- }, async (root) => (await cloudOf(root)).policy.flows);
220
+ return unsyncedRoots(roots,
221
+ // A credential is what sends: a folder with no link file sends with the environment's key.
222
+ async (root) => null !== (await cloudOf(root)).config, async (root) => (await cloudOf(root)).policy.flows);
224
223
  }
@@ -17,7 +17,7 @@ import { sessionIdShape } from '../../surface/tools/tool-kit.js';
17
17
  import { asNumber, asString } from '@reticlehq/core';
18
18
  import { sessionRoot } from '../project/session-root.js';
19
19
  import { MemoryUnavailable, readProjectMemory } from './project-memory.js';
20
- import { MemoryAction, addMemoryStatement, fetchPlatformFlow, pushFlow, setAsideStatement, } from './platform-memory.js';
20
+ import { MEMORY_NOT_LINKED_ADVICE, MemoryAction, addMemoryStatement, fetchPlatformFlow, pushFlow, setAsideStatement, } from './platform-memory.js';
21
21
  import { flowsForSession } from '../../language/flows/flow-store-for-session.js';
22
22
  import { safeSessionTarget } from '../project/session-root.js';
23
23
  import { homedir } from 'node:os';
@@ -32,7 +32,7 @@ const NEEDS_STATEMENT = 'needs `subject` and `statement`';
32
32
  const DEFAULT_LIMIT = 10;
33
33
  /** What to do next, per reason. A refusal that does not say how to fix it is a dead end. */
34
34
  const ADVICE = {
35
- [MemoryUnavailable.NOT_LINKED]: 'this project is not linked to a Reticle workspace, so there is no shared memory to read. Run `npx @reticlehq/server login` in the project to link it.',
35
+ [MemoryUnavailable.NOT_LINKED]: MEMORY_NOT_LINKED_ADVICE,
36
36
  [MemoryUnavailable.DISABLED]: 'this project is linked but memory sync is turned off in .reticle/cloud.json, so nothing is being shared. Enable `sync.memory` to use it.',
37
37
  [MemoryUnavailable.UNREACHABLE]: 'the workspace could not be reached, or refused the key. Nothing is wrong with this project — try again, or check the link with `reticle status`.',
38
38
  [MemoryUnavailable.UNVERIFIED]: 'the server answered, but the answer could not be shown to be about THIS project, so none of it is being reported. This is not an empty corpus: do NOT conclude this project knows nothing. Check the link with `reticle status`, and retry once the workspace is reachable.',
@@ -1,5 +1,5 @@
1
1
  import type { FileSystemPort } from '../project/fs/fs-port.js';
2
- import type { FlowFile } from '@reticlehq/core';
2
+ import { type FlowFile } from '@reticlehq/core';
3
3
  /** What `reticle_memory` can be asked to do. `read` is the default and needs no `action`. */
4
4
  export declare const MemoryAction: {
5
5
  readonly READ: "read";
@@ -16,6 +16,8 @@ export declare const SetAsideRefused: {
16
16
  readonly REFUSED: "refused";
17
17
  };
18
18
  export type PlatformAnswer = Record<string, unknown>;
19
+ /** What an unlinked project is told, on every memory action: the one command, and what it adds. */
20
+ export declare const MEMORY_NOT_LINKED_ADVICE: string;
19
21
  /** One saved flow from the platform: a teammate's, or this project's as the platform holds it. */
20
22
  export declare function fetchPlatformFlow(fs: FileSystemPort, root: string, name: string): Promise<PlatformAnswer>;
21
23
  /** Add one statement to the project's memory. It lands as a proposal; only a passing check proves it. */
@@ -8,6 +8,7 @@
8
8
  import { homedir } from 'node:os';
9
9
  import { cloudFetch, syncFlowToCloud, SyncOutcome } from '../cloud/cloud-sync.js';
10
10
  import { resolveProjectCloud } from '../cloud/cloud-config.js';
11
+ import { CONNECT_COMMAND, DEFAULT_PLATFORM_URL } from '@reticlehq/core';
11
12
  import { MemoryUnavailable } from './project-memory.js';
12
13
  /** What `reticle_memory` can be asked to do. `read` is the default and needs no `action`. */
13
14
  export const MemoryAction = {
@@ -32,6 +33,10 @@ const HTTP_NOT_FOUND = 404;
32
33
  const HTTP_FORBIDDEN = 403;
33
34
  const HTTP_BAD_REQUEST = 400;
34
35
  const unavailable = (reason, error) => ({ error, reason });
36
+ /** What an unlinked project is told, on every memory action: the one command, and what it adds. */
37
+ export const MEMORY_NOT_LINKED_ADVICE = `Run \`${CONNECT_COMMAND}\` in the project folder to link it; add \`--url <origin>\` only for a ` +
38
+ `Reticle platform other than ${DEFAULT_PLATFORM_URL}. Once linked, memory keeps what each run ` +
39
+ 'proved, and every agent and teammate on the project can read it.';
35
40
  const UNREACHABLE = unavailable(MemoryUnavailable.UNREACHABLE, 'the platform could not be reached, or refused this project’s key');
36
41
  /** The link, or the reason there is none. Writes honour `sync.memory` / `sync.flows` like the sync. */
37
42
  async function linked(fs, root, allowed) {
@@ -40,7 +45,10 @@ async function linked(fs, root, allowed) {
40
45
  if (null === config)
41
46
  return {
42
47
  ok: false,
43
- answer: unavailable(MemoryUnavailable.NOT_LINKED, 'this project is not linked to the platform'),
48
+ answer: {
49
+ ...unavailable(MemoryUnavailable.NOT_LINKED, 'this project is not linked to the platform'),
50
+ advice: MEMORY_NOT_LINKED_ADVICE,
51
+ },
44
52
  };
45
53
  if (!allowed(cloud))
46
54
  return {