@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
@@ -1,4 +1,5 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
2
3
  import { z } from 'zod';
3
4
  import { type ToolDeps } from '../tools/tools.js';
4
5
  import type { ToolDef } from '../tools/tools.js';
@@ -17,7 +18,7 @@ export declare const CALL_TOOL_METHOD = "tools/call";
17
18
  * tool with a strict schema (`reticle_context`) failed the whole call on a validating client the
18
19
  * first time one of them rode on it.
19
20
  */
20
- export declare function withSessionEnvelope(_name: string, outputSchema: z.ZodRawShape | undefined): z.ZodRawShape | undefined;
21
+ export declare function withSessionEnvelope(_name: string, outputSchema: Record<string, z.ZodType> | undefined): Record<string, z.ZodType> | undefined;
21
22
  /**
22
23
  * Abbreviations whose internal period is NOT a sentence end.
23
24
  *
@@ -92,10 +93,26 @@ tools?: readonly ToolDef[]): ToolDef[];
92
93
  */
93
94
  export declare function advertisedConfig(tool: ToolDef, advertised: readonly ToolDef[], profile: ToolSurface): {
94
95
  description: string;
95
- inputSchema: z.ZodRawShape;
96
- outputSchema?: z.ZodRawShape;
96
+ inputSchema: Record<string, z.ZodType>;
97
+ outputSchema?: Record<string, z.ZodType>;
98
+ annotations: ToolAnnotations;
97
99
  };
98
100
  export declare function installFriendlyArgErrors(server: McpServer, examples: Map<string, string>, allowedKeys?: Map<string, ReadonlySet<string>>): void;
101
+ /**
102
+ * The daemon's factory for one MCP server for each attach. A proxy's id is its agent identity, so a
103
+ * reconnect from the same proxy is the same owner and keeps its leases. An attach with no proxy id
104
+ * gets a new random id from `createMcpServer`.
105
+ *
106
+ * `instructions` is read for each attach, not once: a project that gets wired while the daemon is
107
+ * alive must stop being told to wire itself on the next agent that connects.
108
+ */
109
+ export declare function mcpServerFactoryFor(deps: ToolDeps, profile: ToolSurface, instructions: () => boolean | Omit<InstructionState, 'advertised'>): (peerSkew?: string, proxyId?: string, agentSession?: string) => McpServer;
110
+ /**
111
+ * The `tools/list` result this server serves on `surface`, built with no daemon and no deps. The
112
+ * proxy answers with it while its daemon starts. The same table, the same per-tool config and the
113
+ * same SDK conversion make it the same list; `proxy-cold-start.test.ts` holds the two equal.
114
+ */
115
+ export declare function staticToolCatalog(surface: ToolSurface): Promise<unknown[]>;
99
116
  export declare function createMcpServer(rawDeps: ToolDeps, profile?: ToolSurface,
100
117
  /**
101
118
  * Has an app ever connected for this project? Decides what the instructions LEAD with.
@@ -5,14 +5,14 @@ import { z } from 'zod';
5
5
  import { isPredicateParam } from '@reticlehq/engine/question/predicate/predicate-eval.js';
6
6
  import { isToonable, resultToToon, MCP_SERVER_NAME } from '@reticlehq/core';
7
7
  import { tableForSurface } from '../tools/tools.js';
8
- import { filterTools, describeToolSurface, TOOL_SURFACE, } from '../tools/tool-surface.js';
8
+ import { filterTools, describeToolSurface, TOOL_SURFACE, toolAnnotations, } from '../tools/tool-surface.js';
9
9
  import { ReticleTool } from '@reticlehq/core';
10
- import { SHARED_PARAM_SHORT } from './shared-params.js';
11
- import { buildDynamicTools } from '../tools/dynamic-tools.js';
10
+ import { sharedParamShort } from './shared-params.js';
11
+ import { buildDynamicTools, descriptionOf } from '../tools/dynamic-tools.js';
12
12
  import { runTool } from '../tools/invoke-tool.js';
13
13
  import { sessionEnvelopeShape, newSnapshotCache } from '../tools/tool-kit.js';
14
- import { buildErrorPayload } from '../tools/error-recovery.js';
15
- import { takeVersionSkewOnto } from '../../command/version/version-nudge.js';
14
+ import { buildErrorPayload, codedRefusalOf } from '../tools/error-recovery.js';
15
+ import { connectionSkew, takeVersionSkewOnto } from '../../command/version/version-nudge.js';
16
16
  import { resultIsError } from './faults/mcp-is-error.js';
17
17
  import { consumerVerdictRefusal, reservedVerdictKeysIn } from './consumer-verdict-guard.js';
18
18
  import { buildServerInstructions } from './server-instructions.js';
@@ -38,7 +38,7 @@ import { SERVER_VERSION } from '../../command/version/identity/server-version.js
38
38
  import { setMcpClientNameHook } from '../../telemetry/feedback-context.js';
39
39
  import { getSessionMetrics } from '../../telemetry/session-metrics.js';
40
40
  import { reportMcpConnected } from '../../telemetry/mcp-connection.js';
41
- import { noteAgentAttached, noteAgentDetached, runAsAgent } from '../../hooks/coding-agents.js';
41
+ import { noteAgentAttached, noteAgentDetached, noteAgentSession, runAsAgent, } from '../../hooks/coding-agents.js';
42
42
  import { parsePredicate } from '@reticlehq/engine/question/predicate/predicate-parse.js';
43
43
  /**
44
44
  * Merge the runtime-spliced envelope into a tool's declared outputSchema so structuredContent
@@ -51,8 +51,9 @@ import { parsePredicate } from '@reticlehq/engine/question/predicate/predicate-p
51
51
  * first time one of them rode on it.
52
52
  */
53
53
  export function withSessionEnvelope(_name, outputSchema) {
54
- if (outputSchema === undefined)
54
+ if (outputSchema === undefined) {
55
55
  return outputSchema;
56
+ }
56
57
  return { ...sessionEnvelopeShape, ...outputSchema };
57
58
  }
58
59
  const SERVER_INFO = { name: MCP_SERVER_NAME, version: SERVER_VERSION };
@@ -124,7 +125,7 @@ const PREDICATE_KINDS = 'Predicate object: { kind, ...fields }. kind is one of e
124
125
  * paying for six.
125
126
  */
126
127
  const PREDICATE_FIELD_GRAMMAR_HINT = ' Bug-catching options: net.count (exact request count — catches double-submit), ' +
127
- 'net.bodyMatches (a JSON field match on what the SERVER answered — say the FIELD (a dotted path reaches nested ones) and its value, ' +
128
+ 'net.bodyMatches (a JSON field match on what the SERVER answered, keyed by the field: { status: "completed" }, never { field, value }; a dotted key reaches a nested field; ' +
128
129
  'because a substring of the body matches key names too), net.requestBodyMatches (the same on what ' +
129
130
  'the UI SENT — the verdict for "applying this filter actually puts it in the payload"), ' +
130
131
  'compare (page text vs server answer, no expected value: { left:{from:"text",scope}, ' +
@@ -149,16 +150,14 @@ function leanZodShape(shape, advertised, predicateAnchor) {
149
150
  for (const [key, schema] of Object.entries(shape)) {
150
151
  if (isPredicateParam(schema)) {
151
152
  // Every tool keeps the kinds; only the navigation hint is anchored.
152
- const compact = z
153
- .record(z.unknown())
154
- .describe(hintSpent ? PREDICATE_KINDS : COMPACT_PREDICATE_DESCRIPTION);
153
+ const compact = z.record(z.string(), z.unknown());
154
+ out[key] = (schema.isOptional() ? compact.optional() : compact).describe(hintSpent ? PREDICATE_KINDS : COMPACT_PREDICATE_DESCRIPTION);
155
155
  hintSpent = true;
156
- out[key] = schema.isOptional() ? compact.optional() : compact;
157
156
  continue;
158
157
  }
159
158
  // Shared boilerplate is documented once in the initialize instructions instead of on every
160
159
  // tool, every turn. `sessionId` alone was 2,076 bytes of identical text across 16 tools.
161
- const shared = SHARED_PARAM_SHORT[key];
160
+ const shared = sharedParamShort(key, schema);
162
161
  if (shared !== undefined) {
163
162
  // `.describe` returns the same schema with new prose, so optionality carries over. Re-wrapping
164
163
  // in `.optional()` would nest one inside another and change the parameter's TYPE, which is a
@@ -168,7 +167,7 @@ function leanZodShape(shape, advertised, predicateAnchor) {
168
167
  }
169
168
  // Through the same rewrite as tool descriptions: a parameter that says "a ref from
170
169
  // reticle_snapshot" names a tool this surface does not have.
171
- const desc = schema.description;
170
+ const desc = descriptionOf(schema);
172
171
  out[key] =
173
172
  'string' === typeof desc
174
173
  ? schema.describe(liveCallText(firstSentence(desc), advertised))
@@ -333,12 +332,14 @@ export function advertisedConfig(tool, advertised, profile) {
333
332
  ? leanZodShape(tool.inputSchema, new Set(advertised.map((each) => each.name)), tool.name === anchor ? undefined : anchor)
334
333
  : tool.inputSchema,
335
334
  ...(outputSchema !== undefined ? { outputSchema } : {}),
335
+ annotations: toolAnnotations(tool.name),
336
336
  };
337
337
  }
338
338
  /** Argument keys the tool never declared. Empty when the tool's shape is unknown to us. */
339
339
  function unknownKeys(args, allowed) {
340
- if (allowed === undefined || typeof args !== 'object' || null === args)
340
+ if (allowed === undefined || typeof args !== 'object' || null === args) {
341
341
  return [];
342
+ }
342
343
  return Object.keys(args).filter((key) => !allowed.has(key));
343
344
  }
344
345
  export function installFriendlyArgErrors(server, examples, allowedKeys = new Map()) {
@@ -404,12 +405,14 @@ export function installFriendlyArgErrors(server, examples, allowedKeys = new Map
404
405
  * the predicate is not what broke.
405
406
  */
406
407
  function predicateMessage(args) {
407
- if (typeof args !== 'object' || null === args)
408
+ if (typeof args !== 'object' || null === args) {
408
409
  return undefined;
410
+ }
409
411
  const record = args;
410
412
  const predicate = record['predicate'] ?? record['until'];
411
- if (predicate === undefined)
413
+ if (predicate === undefined) {
412
414
  return undefined;
415
+ }
413
416
  try {
414
417
  parsePredicate(predicate);
415
418
  return undefined;
@@ -454,8 +457,9 @@ function installUnadvertisedToolHelp(server, advertised, known) {
454
457
  const help = name === undefined ? undefined : unadvertisedToolHelp(name, advertised, known);
455
458
  // A failed RESULT, not a JSON-RPC error: a client that reads only `result` rendered the error
456
459
  // as an empty success, and the agent believed `reticle_flow_save` had saved its flow.
457
- if (help !== undefined)
460
+ if (help !== undefined) {
458
461
  return { content: [{ type: 'text', text: help }], isError: true };
462
+ }
459
463
  return original(request, extra);
460
464
  });
461
465
  }
@@ -477,14 +481,32 @@ function asCallOf(request, tool, action) {
477
481
  }
478
482
  /** The requested tool name, when the request has the shape we expect. */
479
483
  function toolNameOf(request) {
480
- if (typeof request !== 'object' || null === request)
484
+ if (typeof request !== 'object' || null === request) {
481
485
  return undefined;
486
+ }
482
487
  const params = request.params;
483
- if (typeof params !== 'object' || null === params)
488
+ if (typeof params !== 'object' || null === params) {
484
489
  return undefined;
490
+ }
485
491
  const name = params.name;
486
492
  return 'string' === typeof name ? name : undefined;
487
493
  }
494
+ /**
495
+ * The daemon's factory for one MCP server for each attach. A proxy's id is its agent identity, so a
496
+ * reconnect from the same proxy is the same owner and keeps its leases. An attach with no proxy id
497
+ * gets a new random id from `createMcpServer`.
498
+ *
499
+ * `instructions` is read for each attach, not once: a project that gets wired while the daemon is
500
+ * alive must stop being told to wire itself on the next agent that connects.
501
+ */
502
+ export function mcpServerFactoryFor(deps, profile, instructions) {
503
+ return (peerSkew, proxyId, agentSession) => createMcpServer({
504
+ ...deps,
505
+ ...(peerSkew === undefined ? {} : { peerSkew: connectionSkew(peerSkew) }),
506
+ ...(proxyId === undefined ? {} : { attachId: proxyId }),
507
+ ...(agentSession === undefined ? {} : { agentSession }),
508
+ }, profile, instructions());
509
+ }
488
510
  /**
489
511
  * The tools this codebase ships. Anything outside it came from a host via the `tools` parameter, and
490
512
  * is held to the verdict rule below.
@@ -500,6 +522,35 @@ function toolNameOf(request) {
500
522
  * covered the day it is added instead of the day somebody notices.
501
523
  */
502
524
  const FIRST_PARTY_TOOL_NAMES = new Set(Object.values(ReticleTool));
525
+ /**
526
+ * The `tools/list` result this server serves on `surface`, built with no daemon and no deps. The
527
+ * proxy answers with it while its daemon starts. The same table, the same per-tool config and the
528
+ * same SDK conversion make it the same list; `proxy-cold-start.test.ts` holds the two equal.
529
+ */
530
+ export async function staticToolCatalog(surface) {
531
+ const [{ Client }, { InMemoryTransport }] = await Promise.all([
532
+ import('@modelcontextprotocol/sdk/client/index.js'),
533
+ import('@modelcontextprotocol/sdk/inMemory.js'),
534
+ ]);
535
+ const server = new McpServer(SERVER_INFO);
536
+ const register = server.registerTool.bind(server);
537
+ const advertised = advertisedTools(surface);
538
+ for (const tool of advertised) {
539
+ // Never called: this server only lists.
540
+ register(tool.name, advertisedConfig(tool, advertised, surface), () => ({ content: [] }));
541
+ }
542
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
543
+ await server.connect(serverTransport);
544
+ const client = new Client(SERVER_INFO);
545
+ await client.connect(clientTransport);
546
+ try {
547
+ return (await client.listTools()).tools;
548
+ }
549
+ finally {
550
+ await client.close();
551
+ await server.close();
552
+ }
553
+ }
503
554
  export function createMcpServer(rawDeps, profile = TOOL_SURFACE.DEFAULT,
504
555
  /**
505
556
  * Has an app ever connected for this project? Decides what the instructions LEAD with.
@@ -530,6 +581,12 @@ tools = tableForSurface(profile)) {
530
581
  * store keeps it.
531
582
  */
532
583
  const deps = { snapshots: newSnapshotCache(), attachId: randomUUID(), ...rawDeps };
584
+ // A proxy that reconnects brings its own id as `attachId`, so this is the same owner coming back:
585
+ // take its leases out of their grace. A new random id owns no leases, so this does nothing.
586
+ deps.pool?.reattachOwner(deps.attachId);
587
+ if (deps.agentSession !== undefined) {
588
+ noteAgentSession(deps.attachId, deps.agentSession);
589
+ }
533
590
  const encoding = (process.env[ENCODING_ENV] ?? '').toLowerCase();
534
591
  // Which surface this daemon advertises. The 18-tool default and the 48-tool full surface are
535
592
  // different products from inside an agent's context, so outcomes are only comparable when the
@@ -558,8 +615,9 @@ tools = tableForSurface(profile)) {
558
615
  */
559
616
  server.server.oninitialized = () => {
560
617
  const info = server.server.getClientVersion();
561
- if (info?.name === undefined)
618
+ if (info?.name === undefined) {
562
619
  return;
620
+ }
563
621
  getSessionMetrics().recordClient(info.name, info.version);
564
622
  // So the HUD can say which agent is connected, and credit a note to the one that read it.
565
623
  noteAgentAttached(deps.attachId, info.name);
@@ -567,9 +625,13 @@ tools = tableForSurface(profile)) {
567
625
  // the first moment it can carry WHO. It used to fire from the SSE `connect()` resolution, which
568
626
  // happens before the handshake — so every connect row was anonymous, and the field meant to say
569
627
  // which agent client converts best was empty on every event ever sent.
570
- void runAsAgent(deps.attachId, () => Promise.resolve(reportMcpConnected(info.name)));
628
+ void runAsAgent(deps.attachId, () => Promise.resolve(reportMcpConnected(info.name, deps.attachId)));
629
+ };
630
+ server.server.onclose = () => {
631
+ noteAgentDetached(deps.attachId);
632
+ // Start the leases' short grace. A reconnect from the same proxy takes them back (see above).
633
+ deps.pool?.detachOwner(deps.attachId);
571
634
  };
572
- server.server.onclose = () => noteAgentDetached(deps.attachId);
573
635
  // Which agent is on the other end, for a feedback report. Registered as a lazy hook rather than
574
636
  // read here: the handshake has not happened yet at construction time, and a report filed twenty
575
637
  // tool calls later is exactly when we want the answer.
@@ -578,8 +640,9 @@ tools = tableForSurface(profile)) {
578
640
  // Still records, because a client that skipped the handshake path above (a replayed initialize
579
641
  // through the proxy, a transport that reconnects) is a client we would otherwise lose entirely.
580
642
  // `recordClient` is idempotent per name.
581
- if (info?.name !== undefined)
643
+ if (info?.name !== undefined) {
582
644
  getSessionMetrics().recordClient(info.name, info.version);
645
+ }
583
646
  return info === undefined ? undefined : { name: info.name, version: info.version };
584
647
  });
585
648
  // Cast once to our bridge type so every per-tool call site is typed without `any`.
@@ -706,7 +769,7 @@ tools = tableForSurface(profile)) {
706
769
  content: [
707
770
  {
708
771
  type: 'text',
709
- text: JSON.stringify(liveCallValues(takeVersionSkewOnto(buildErrorPayload(message), deps.peerSkew), advertisedNames)),
772
+ text: JSON.stringify(liveCallValues(takeVersionSkewOnto(buildErrorPayload(message, codedRefusalOf(error)), deps.peerSkew), advertisedNames)),
710
773
  },
711
774
  ],
712
775
  };
@@ -8,3 +8,47 @@ export declare function isHarnessDriveCall(msg: {
8
8
  * call as if it had not run pays for a second drive.
9
9
  */
10
10
  export declare const MSG_DRIVE_MAY_BE_RUNNING: string;
11
+ /**
12
+ * JSON-RPC requests forwarded on behalf of the client that have not been answered yet.
13
+ *
14
+ * Only used at shutdown, where it is the difference between "the client closed stdin" and "the client
15
+ * closed stdin and we dropped its `tools/list` on the floor while reporting exit 0". A request has an
16
+ * id AND a method; a notification has no id and is owed nothing; a response has an id and no method.
17
+ */
18
+ export declare class PendingRequests {
19
+ #private;
20
+ observeOutbound(line: string): void;
21
+ /** Whether this unanswered request asked for a Harness drive. */
22
+ isDrive(id: unknown): boolean;
23
+ observeInbound(line: string): void;
24
+ get unanswered(): string[];
25
+ /**
26
+ * Claim ONE unanswered id, so exactly one path may answer it. False means somebody already did —
27
+ * a duplicate response to one id is a protocol violation, not a belt-and-braces retry.
28
+ */
29
+ take(id: unknown): boolean;
30
+ /** Take every unanswered id and forget them, so the same request is failed at most once. */
31
+ drain(): string[];
32
+ /** Forget which requests were drives, once their replies are written. */
33
+ clearDrives(): void;
34
+ }
35
+ /**
36
+ * JSON-RPC errors for every request that was in flight when the stream died.
37
+ *
38
+ * These are unrecoverable BY CONSTRUCTION: the daemon builds a fresh MCP session per SSE
39
+ * connection, so the reconnected session has never seen them. Re-sending them is not an option
40
+ * either — a `reticle_act` that already clicked would click twice, and inventing a second action in
41
+ * somebody's app is worse than reporting a failure.
42
+ *
43
+ * So the proxy answers them itself, under each request's own id. Found by brute force: killing the
44
+ * daemon under a live client left 20 of 20 concurrent calls hanging until the client's own timeout,
45
+ * with the MCP server still perfectly alive. An agent cannot tell that apart from "still working".
46
+ */
47
+ export declare function streamLossReplies(pending: PendingRequests, reason: string, nextStep?: string): string[];
48
+ /**
49
+ * The one shape every "the transport ate your call" answer takes, whichever leg died.
50
+ *
51
+ * `nextStep` is appended when the caller knows one. Without it this reply describes a condition and
52
+ * stops there, which is all a first-run user got out of their first ever tool call.
53
+ */
54
+ export declare function transportLossReply(id: unknown, reason: string, nextStep?: string, drive?: boolean): string;
@@ -1,19 +1,27 @@
1
- /** Which proxied calls are Harness drives, and what to tell an agent whose drive call was lost. */
1
+ /**
2
+ * The proxied calls still in flight, which of them are Harness drives, and what each one is told
3
+ * when the transport under it is lost.
4
+ */
2
5
  import { ReticleTool } from '@reticlehq/core';
6
+ import { parseLine as parseJsonRpc } from './proxy-handshake.js';
3
7
  const TOOLS_CALL = 'tools/call';
4
8
  const EXPLORE_ACTION = 'explore';
5
9
  /** A `tools/call` of reticle_verify explore, directly or through the reticle_run hatch. */
6
10
  export function isHarnessDriveCall(msg) {
7
- if (TOOLS_CALL !== msg.method)
11
+ if (TOOLS_CALL !== msg.method) {
8
12
  return false;
13
+ }
9
14
  const params = (msg.params ?? {});
10
15
  const args = (params.arguments ?? {});
11
- if (ReticleTool.VERIFY_EXPLORE === params.name)
16
+ if (ReticleTool.VERIFY_EXPLORE === params.name) {
12
17
  return true;
13
- if (ReticleTool.VERIFY === params.name)
18
+ }
19
+ if (ReticleTool.VERIFY === params.name) {
14
20
  return EXPLORE_ACTION === args['action'];
15
- if (ReticleTool.RUN !== params.name)
21
+ }
22
+ if (ReticleTool.RUN !== params.name) {
16
23
  return false;
24
+ }
17
25
  const inner = (args['args'] ?? {});
18
26
  return (ReticleTool.VERIFY_EXPLORE === args['tool'] ||
19
27
  (ReticleTool.VERIFY === args['tool'] && EXPLORE_ACTION === inner['action']));
@@ -25,3 +33,104 @@ export function isHarnessDriveCall(msg) {
25
33
  export const MSG_DRIVE_MAY_BE_RUNNING = `The Harness drive this call started or polled may still be running in the daemon. Once ` +
26
34
  `Reticle reconnects, call ${ReticleTool.VERIFY} {action:"explore"} with no runId to get the ` +
27
35
  `running drive back (it will not start a second), then poll it by runId.`;
36
+ /**
37
+ * JSON-RPC requests forwarded on behalf of the client that have not been answered yet.
38
+ *
39
+ * Only used at shutdown, where it is the difference between "the client closed stdin" and "the client
40
+ * closed stdin and we dropped its `tools/list` on the floor while reporting exit 0". A request has an
41
+ * id AND a method; a notification has no id and is owed nothing; a response has an id and no method.
42
+ */
43
+ export class PendingRequests {
44
+ #ids = new Set();
45
+ /** The pending requests that start or poll a Harness drive, which outlives a lost connection. */
46
+ #drives = new Set();
47
+ observeOutbound(line) {
48
+ const msg = parseJsonRpc(line);
49
+ if (null === msg || msg.id === undefined || msg.method === undefined) {
50
+ return;
51
+ }
52
+ const id = JSON.stringify(msg.id);
53
+ this.#ids.add(id);
54
+ if (isHarnessDriveCall(msg)) {
55
+ this.#drives.add(id);
56
+ }
57
+ }
58
+ /** Whether this unanswered request asked for a Harness drive. */
59
+ isDrive(id) {
60
+ return this.#drives.has(JSON.stringify(id));
61
+ }
62
+ observeInbound(line) {
63
+ const msg = parseJsonRpc(line);
64
+ if (null === msg || msg.id === undefined) {
65
+ return;
66
+ }
67
+ this.#ids.delete(JSON.stringify(msg.id));
68
+ }
69
+ get unanswered() {
70
+ return [...this.#ids];
71
+ }
72
+ /**
73
+ * Claim ONE unanswered id, so exactly one path may answer it. False means somebody already did —
74
+ * a duplicate response to one id is a protocol violation, not a belt-and-braces retry.
75
+ */
76
+ take(id) {
77
+ this.#drives.delete(JSON.stringify(id));
78
+ return this.#ids.delete(JSON.stringify(id));
79
+ }
80
+ /** Take every unanswered id and forget them, so the same request is failed at most once. */
81
+ drain() {
82
+ const ids = [...this.#ids];
83
+ this.#ids.clear();
84
+ return ids;
85
+ }
86
+ /** Forget which requests were drives, once their replies are written. */
87
+ clearDrives() {
88
+ this.#drives.clear();
89
+ }
90
+ }
91
+ /**
92
+ * JSON-RPC errors for every request that was in flight when the stream died.
93
+ *
94
+ * These are unrecoverable BY CONSTRUCTION: the daemon builds a fresh MCP session per SSE
95
+ * connection, so the reconnected session has never seen them. Re-sending them is not an option
96
+ * either — a `reticle_act` that already clicked would click twice, and inventing a second action in
97
+ * somebody's app is worse than reporting a failure.
98
+ *
99
+ * So the proxy answers them itself, under each request's own id. Found by brute force: killing the
100
+ * daemon under a live client left 20 of 20 concurrent calls hanging until the client's own timeout,
101
+ * with the MCP server still perfectly alive. An agent cannot tell that apart from "still working".
102
+ */
103
+ export function streamLossReplies(pending, reason, nextStep) {
104
+ const replies = pending.drain().map((id) => {
105
+ const parsed = JSON.parse(id);
106
+ return transportLossReply(parsed, reason, nextStep, pending.isDrive(parsed));
107
+ });
108
+ pending.clearDrives();
109
+ return replies;
110
+ }
111
+ /**
112
+ * -32001: a server-defined error. Not -32603 (internal): nothing went wrong INSIDE the call, the
113
+ * transport under it went away, and the distinction is what tells a caller the action may be safe
114
+ * to retry.
115
+ */
116
+ const TRANSPORT_LOSS_CODE = -32001;
117
+ /**
118
+ * The one shape every "the transport ate your call" answer takes, whichever leg died.
119
+ *
120
+ * `nextStep` is appended when the caller knows one. Without it this reply describes a condition and
121
+ * stops there, which is all a first-run user got out of their first ever tool call.
122
+ */
123
+ export function transportLossReply(id, reason, nextStep, drive = false) {
124
+ return JSON.stringify({
125
+ jsonrpc: '2.0',
126
+ id,
127
+ error: {
128
+ code: TRANSPORT_LOSS_CODE,
129
+ message: (drive
130
+ ? `the daemon connection dropped (${reason}) before this call was answered. ${MSG_DRIVE_MAY_BE_RUNNING}`
131
+ : `the daemon connection dropped (${reason}) before this call was answered — it did NOT ` +
132
+ 'complete. Reticle is recovering the connection; retry if the action is safe to repeat.') +
133
+ (nextStep === undefined ? '' : ` ${nextStep}`),
134
+ },
135
+ });
136
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The daemon that `reticle mcp` starts beside the proxy. `ready` settles when the start is done,
3
+ * with the port to dial. `spawning` resolves when a new daemon process is started: the port was
4
+ * free, so no stranger holds it, and the proxy can answer the handshake at once with `tools()`,
5
+ * the daemon's own tool list built in this process.
6
+ */
7
+ export interface ProxyBoot {
8
+ readonly ready: Promise<number | void>;
9
+ readonly spawning: Promise<void>;
10
+ readonly tools: () => Promise<unknown[]>;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -19,11 +19,17 @@ export declare function probeDaemon(port: number, host?: string): Promise<boolea
19
19
  *
20
20
  * Probes are injected (defaulting to the real pair) so the rule is testable without a socket, which
21
21
  * is the same shape `probePresence`'s own callers use.
22
+ *
23
+ * `alive` says if the daemon process in the pid file still runs. When it does not, no daemon can
24
+ * come up, so the wait stops at once and does not poll to the deadline.
22
25
  */
23
26
  export declare function waitForDaemon(port: number, probes?: {
24
27
  tcpOpen: (port: number) => Promise<boolean>;
25
28
  status: (port: number) => Promise<unknown>;
29
+ alive?: () => boolean;
26
30
  }): Promise<void>;
31
+ /** True while the process in the daemon pid file for `port` runs. */
32
+ export declare function daemonProcessAlive(port: number): boolean;
27
33
  /**
28
34
  * Poll until `/status` on `port` answers as a Reticle daemon; false once the bound passes.
29
35
  *
@@ -2,6 +2,7 @@ import * as net from 'node:net';
2
2
  import { LOOPBACK_HOST } from '@reticlehq/core';
3
3
  import { describePresence, presenceIsUsable, probePresence, PortPresence, } from '../../../command/daemon/binding/port-presence.js';
4
4
  import { fetchStatus } from '../../../command/daemon/binding/daemon-status-probe.js';
5
+ import { isAlive, readPid } from '../../../command/daemon/daemon.js';
5
6
  const DEFAULT_DAEMON_READY_TIMEOUT_MS = 10_000;
6
7
  /**
7
8
  * How long to wait for the spawned daemon's port to accept connections before giving up. The default
@@ -55,8 +56,11 @@ export function probeDaemon(port, host = LOOPBACK_HOST) {
55
56
  *
56
57
  * Probes are injected (defaulting to the real pair) so the rule is testable without a socket, which
57
58
  * is the same shape `probePresence`'s own callers use.
59
+ *
60
+ * `alive` says if the daemon process in the pid file still runs. When it does not, no daemon can
61
+ * come up, so the wait stops at once and does not poll to the deadline.
58
62
  */
59
- export async function waitForDaemon(port, probes = { tcpOpen: probeDaemon, status: fetchStatus }) {
63
+ export async function waitForDaemon(port, probes = { tcpOpen: probeDaemon, status: fetchStatus, alive: () => daemonProcessAlive(port) }) {
60
64
  const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
61
65
  let attempt = 0;
62
66
  let presence = PortPresence.FREE;
@@ -64,6 +68,10 @@ export async function waitForDaemon(port, probes = { tcpOpen: probeDaemon, statu
64
68
  presence = await probePresence(port, probes);
65
69
  if (presenceIsUsable(presence))
66
70
  return;
71
+ if (false === probes.alive?.()) {
72
+ throw new Error(`the reticle daemon on port ${String(port)} exited before it was ready — ` +
73
+ describePresence(presence, port));
74
+ }
67
75
  attempt++;
68
76
  await delay(daemonPollDelayMs(attempt));
69
77
  }
@@ -73,6 +81,11 @@ export async function waitForDaemon(port, probes = { tcpOpen: probeDaemon, statu
73
81
  throw new Error(`reticle daemon did not become ready on port ${String(port)} within ` +
74
82
  `${String(DAEMON_READY_TIMEOUT_MS)}ms — ${describePresence(presence, port)}`);
75
83
  }
84
+ /** True while the process in the daemon pid file for `port` runs. */
85
+ export function daemonProcessAlive(port) {
86
+ const pid = readPid(port);
87
+ return null !== pid && isAlive(pid);
88
+ }
76
89
  /** How long a spawned daemon gets to bind before `serve`, `restart` or `init` gives up on it. */
77
90
  const BIND_TIMEOUT_MS = 15_000;
78
91
  const BIND_POLL_MS = 150;
@@ -8,6 +8,15 @@
8
8
  * connected, initialized and toolless until a human notices.
9
9
  */
10
10
  export declare const TOOLS_CHANGED_NOTIFICATION = "notifications/tools/list_changed";
11
+ interface JsonRpcLike {
12
+ id?: unknown;
13
+ method?: unknown;
14
+ params?: {
15
+ protocolVersion?: unknown;
16
+ };
17
+ }
18
+ /** A line as a JSON-RPC object, or null when it is not JSON or not an object. */
19
+ export declare function parseLine(line: string): JsonRpcLike | null;
11
20
  /** True when this queued client line is the handshake we answered ourselves. */
12
21
  export declare function isHandshakeLine(line: string): boolean;
13
22
  export declare function localInitializeResponse(line: string,
@@ -63,8 +72,11 @@ export declare function toolsChangedNotification(catalogWasLocal: boolean): stri
63
72
  * on the initialize result, which SUCCEEDED, and every host we target puts it in front of the model.
64
73
  * A tool description cannot reach an agent that was handed no tools.
65
74
  *
66
- * The healthy block is kept underneath rather than replaced: an agent that IS about to get a working
67
- * surface (the common case — the first call wakes a daemon) still needs to know how to drive it.
75
+ * The healthy block is kept underneath: an agent that IS about to get a working surface (the common
76
+ * case — the first call wakes a daemon) still needs to know how to drive it. Only its part up to the
77
+ * writing rule is kept. Claude Code shows about the first 4,096 characters, and the notice plus the
78
+ * whole block came to about 4,600, so the client cut the writing rule. The text says what it left
79
+ * out, so the cut is not silent.
68
80
  */
69
81
  export declare function degradedInstructions(healthy: string, port: number, reason: string): string;
70
82
  /**
@@ -105,4 +117,24 @@ interface DrainedLines {
105
117
  * the concatenation alone. Only a chunk that actually carries a newline pays for a scan.
106
118
  */
107
119
  export declare function drainLines(buffer: string, chunk: string, maxLineBytes?: number, discarding?: boolean): DrainedLines;
120
+ /** JSON-RPC id the proxy uses for its own replayed `initialize` — never one the client could send. */
121
+ export declare const RECONNECT_INITIALIZE_ID = "__reticle_proxy_reinit";
122
+ /**
123
+ * Makes a reconnect invisible to the client.
124
+ *
125
+ * The daemon builds a FRESH `McpServer` per SSE connection, so a reconnected session has never seen
126
+ * the client's `initialize` — every subsequent `tools/call` would hit an uninitialized server. The
127
+ * proxy therefore remembers the handshake the client sent once and replays it into each new session.
128
+ * The replay is re-issued under a reserved id so the daemon's response to it can be dropped rather
129
+ * than reaching the client's stdout, where a duplicate JSON-RPC id would be a protocol violation.
130
+ */
131
+ export declare class HandshakeReplay {
132
+ #private;
133
+ /** Record one outbound client line. Cheap enough to call for every stdin message. */
134
+ observeOutbound(line: string): void;
135
+ /** Lines to POST into a freshly-established session, before any queued client traffic. */
136
+ replayLines(): string[];
137
+ /** True when this inbound daemon line is the echo of our replayed handshake and must not be forwarded. */
138
+ shouldSuppressInbound(line: string): boolean;
139
+ }
108
140
  export {};