@edspencer/paddock 0.0.1 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/README.md +69 -0
  2. package/install-notice.mjs +29 -0
  3. package/package.json +58 -5
  4. package/packages/server/dist/adoptable.d.ts +127 -0
  5. package/packages/server/dist/adoptable.js +315 -0
  6. package/packages/server/dist/app.d.ts +67 -0
  7. package/packages/server/dist/app.js +325 -0
  8. package/packages/server/dist/archive.d.ts +42 -0
  9. package/packages/server/dist/archive.js +125 -0
  10. package/packages/server/dist/attachments-config.d.ts +114 -0
  11. package/packages/server/dist/attachments-config.js +165 -0
  12. package/packages/server/dist/attachments-hint.d.ts +53 -0
  13. package/packages/server/dist/attachments-hint.js +140 -0
  14. package/packages/server/dist/attachments.d.ts +33 -0
  15. package/packages/server/dist/attachments.js +117 -0
  16. package/packages/server/dist/auth.d.ts +84 -0
  17. package/packages/server/dist/auth.js +247 -0
  18. package/packages/server/dist/background.d.ts +36 -0
  19. package/packages/server/dist/background.js +137 -0
  20. package/packages/server/dist/bind-safety.d.ts +56 -0
  21. package/packages/server/dist/bind-safety.js +51 -0
  22. package/packages/server/dist/brand.d.ts +44 -0
  23. package/packages/server/dist/brand.js +88 -0
  24. package/packages/server/dist/chat-dto.d.ts +210 -0
  25. package/packages/server/dist/chat-dto.js +308 -0
  26. package/packages/server/dist/cli/import-chats.d.ts +21 -0
  27. package/packages/server/dist/cli/import-chats.js +197 -0
  28. package/packages/server/dist/cli/paddock.d.ts +2 -0
  29. package/packages/server/dist/cli/paddock.js +247 -0
  30. package/packages/server/dist/concurrency.d.ts +57 -0
  31. package/packages/server/dist/concurrency.js +71 -0
  32. package/packages/server/dist/config.d.ts +538 -0
  33. package/packages/server/dist/config.js +625 -0
  34. package/packages/server/dist/curation-config.d.ts +55 -0
  35. package/packages/server/dist/curation-config.js +64 -0
  36. package/packages/server/dist/environment-prompt.d.ts +52 -0
  37. package/packages/server/dist/environment-prompt.js +61 -0
  38. package/packages/server/dist/event-bus.d.ts +87 -0
  39. package/packages/server/dist/event-bus.js +48 -0
  40. package/packages/server/dist/git.d.ts +128 -0
  41. package/packages/server/dist/git.js +446 -0
  42. package/packages/server/dist/github-auth.d.ts +44 -0
  43. package/packages/server/dist/github-auth.js +169 -0
  44. package/packages/server/dist/herdctl-agent-config.d.ts +98 -0
  45. package/packages/server/dist/herdctl-agent-config.js +331 -0
  46. package/packages/server/dist/herdctl-agent-names.d.ts +172 -0
  47. package/packages/server/dist/herdctl-agent-names.js +228 -0
  48. package/packages/server/dist/herdctl-jobs-index.d.ts +65 -0
  49. package/packages/server/dist/herdctl-jobs-index.js +225 -0
  50. package/packages/server/dist/herdctl-jobs.d.ts +98 -0
  51. package/packages/server/dist/herdctl-jobs.js +225 -0
  52. package/packages/server/dist/herdctl.d.ts +623 -0
  53. package/packages/server/dist/herdctl.js +1279 -0
  54. package/packages/server/dist/hook-config.d.ts +217 -0
  55. package/packages/server/dist/hook-config.js +224 -0
  56. package/packages/server/dist/http-bytes.d.ts +24 -0
  57. package/packages/server/dist/http-bytes.js +52 -0
  58. package/packages/server/dist/index.d.ts +1 -0
  59. package/packages/server/dist/index.js +14 -0
  60. package/packages/server/dist/instance-config.d.ts +143 -0
  61. package/packages/server/dist/instance-config.js +385 -0
  62. package/packages/server/dist/localcommand.d.ts +23 -0
  63. package/packages/server/dist/localcommand.js +140 -0
  64. package/packages/server/dist/management-auth.d.ts +99 -0
  65. package/packages/server/dist/management-auth.js +165 -0
  66. package/packages/server/dist/management-config.d.ts +164 -0
  67. package/packages/server/dist/management-config.js +286 -0
  68. package/packages/server/dist/management-mcp-server.d.ts +20 -0
  69. package/packages/server/dist/management-mcp-server.js +95 -0
  70. package/packages/server/dist/management-metadata.d.ts +67 -0
  71. package/packages/server/dist/management-metadata.js +56 -0
  72. package/packages/server/dist/management-ops.d.ts +110 -0
  73. package/packages/server/dist/management-ops.js +449 -0
  74. package/packages/server/dist/management-policy.d.ts +172 -0
  75. package/packages/server/dist/management-policy.js +259 -0
  76. package/packages/server/dist/message-provenance.d.ts +112 -0
  77. package/packages/server/dist/message-provenance.js +260 -0
  78. package/packages/server/dist/models.d.ts +164 -0
  79. package/packages/server/dist/models.js +208 -0
  80. package/packages/server/dist/openapi.d.ts +66 -0
  81. package/packages/server/dist/openapi.js +183 -0
  82. package/packages/server/dist/parent-detach.d.ts +30 -0
  83. package/packages/server/dist/parent-detach.js +109 -0
  84. package/packages/server/dist/preload.d.ts +43 -0
  85. package/packages/server/dist/preload.js +55 -0
  86. package/packages/server/dist/project-files.d.ts +76 -0
  87. package/packages/server/dist/project-files.js +191 -0
  88. package/packages/server/dist/project-mime.d.ts +35 -0
  89. package/packages/server/dist/project-mime.js +55 -0
  90. package/packages/server/dist/project-paths.d.ts +88 -0
  91. package/packages/server/dist/project-paths.js +126 -0
  92. package/packages/server/dist/project-types.d.ts +316 -0
  93. package/packages/server/dist/project-types.js +48 -0
  94. package/packages/server/dist/projects.d.ts +202 -0
  95. package/packages/server/dist/projects.js +894 -0
  96. package/packages/server/dist/queued-message.d.ts +35 -0
  97. package/packages/server/dist/queued-message.js +116 -0
  98. package/packages/server/dist/read-state.d.ts +46 -0
  99. package/packages/server/dist/read-state.js +139 -0
  100. package/packages/server/dist/recovery-config.d.ts +111 -0
  101. package/packages/server/dist/recovery-config.js +105 -0
  102. package/packages/server/dist/recovery.d.ts +262 -0
  103. package/packages/server/dist/recovery.js +490 -0
  104. package/packages/server/dist/resume-drain.d.ts +122 -0
  105. package/packages/server/dist/resume-drain.js +158 -0
  106. package/packages/server/dist/route-context.d.ts +168 -0
  107. package/packages/server/dist/route-context.js +39 -0
  108. package/packages/server/dist/route-errors.d.ts +8 -0
  109. package/packages/server/dist/route-errors.js +13 -0
  110. package/packages/server/dist/routes/chats.d.ts +12 -0
  111. package/packages/server/dist/routes/chats.js +1406 -0
  112. package/packages/server/dist/routes/git.d.ts +13 -0
  113. package/packages/server/dist/routes/git.js +236 -0
  114. package/packages/server/dist/routes/mcp.d.ts +83 -0
  115. package/packages/server/dist/routes/mcp.js +250 -0
  116. package/packages/server/dist/routes/meta.d.ts +15 -0
  117. package/packages/server/dist/routes/meta.js +447 -0
  118. package/packages/server/dist/routes/projects.d.ts +25 -0
  119. package/packages/server/dist/routes/projects.js +736 -0
  120. package/packages/server/dist/routes/triggers.d.ts +21 -0
  121. package/packages/server/dist/routes/triggers.js +324 -0
  122. package/packages/server/dist/routes/workspace-mount.d.ts +48 -0
  123. package/packages/server/dist/routes/workspace-mount.js +32 -0
  124. package/packages/server/dist/routes.d.ts +26 -0
  125. package/packages/server/dist/routes.js +29 -0
  126. package/packages/server/dist/run-provenance.d.ts +109 -0
  127. package/packages/server/dist/run-provenance.js +206 -0
  128. package/packages/server/dist/runs.d.ts +92 -0
  129. package/packages/server/dist/runs.js +72 -0
  130. package/packages/server/dist/schedule-config.d.ts +68 -0
  131. package/packages/server/dist/schedule-config.js +75 -0
  132. package/packages/server/dist/schedule-session.d.ts +34 -0
  133. package/packages/server/dist/schedule-session.js +120 -0
  134. package/packages/server/dist/self-mcp-descriptions.d.ts +20 -0
  135. package/packages/server/dist/self-mcp-descriptions.js +145 -0
  136. package/packages/server/dist/self-mcp-read.d.ts +15 -0
  137. package/packages/server/dist/self-mcp-read.js +97 -0
  138. package/packages/server/dist/self-mcp-triggers.d.ts +30 -0
  139. package/packages/server/dist/self-mcp-triggers.js +144 -0
  140. package/packages/server/dist/self-mcp-types.d.ts +248 -0
  141. package/packages/server/dist/self-mcp-types.js +10 -0
  142. package/packages/server/dist/self-mcp-util.d.ts +73 -0
  143. package/packages/server/dist/self-mcp-util.js +157 -0
  144. package/packages/server/dist/self-mcp-write.d.ts +35 -0
  145. package/packages/server/dist/self-mcp-write.js +215 -0
  146. package/packages/server/dist/self-mcp.d.ts +95 -0
  147. package/packages/server/dist/self-mcp.js +448 -0
  148. package/packages/server/dist/send-file-mcp.d.ts +46 -0
  149. package/packages/server/dist/send-file-mcp.js +281 -0
  150. package/packages/server/dist/session-hub.d.ts +186 -0
  151. package/packages/server/dist/session-hub.js +291 -0
  152. package/packages/server/dist/spawn-capability.d.ts +99 -0
  153. package/packages/server/dist/spawn-capability.js +106 -0
  154. package/packages/server/dist/star.d.ts +26 -0
  155. package/packages/server/dist/star.js +94 -0
  156. package/packages/server/dist/start.d.ts +1 -0
  157. package/packages/server/dist/start.js +21 -0
  158. package/packages/server/dist/subagents.d.ts +337 -0
  159. package/packages/server/dist/subagents.js +626 -0
  160. package/packages/server/dist/sweep.d.ts +122 -0
  161. package/packages/server/dist/sweep.js +696 -0
  162. package/packages/server/dist/tooldetails.d.ts +33 -0
  163. package/packages/server/dist/tooldetails.js +466 -0
  164. package/packages/server/dist/transcribe.d.ts +69 -0
  165. package/packages/server/dist/transcribe.js +119 -0
  166. package/packages/server/dist/transcripts.d.ts +42 -0
  167. package/packages/server/dist/transcripts.js +164 -0
  168. package/packages/server/dist/trigger-config.d.ts +278 -0
  169. package/packages/server/dist/trigger-config.js +417 -0
  170. package/packages/server/dist/trigger-runtime.d.ts +94 -0
  171. package/packages/server/dist/trigger-runtime.js +82 -0
  172. package/packages/server/dist/trigger-session.d.ts +34 -0
  173. package/packages/server/dist/trigger-session.js +122 -0
  174. package/packages/server/dist/triggers.d.ts +83 -0
  175. package/packages/server/dist/triggers.js +124 -0
  176. package/packages/server/dist/trusted-proxy.d.ts +57 -0
  177. package/packages/server/dist/trusted-proxy.js +157 -0
  178. package/packages/server/dist/turn-notice.d.ts +154 -0
  179. package/packages/server/dist/turn-notice.js +271 -0
  180. package/packages/server/dist/unread.d.ts +36 -0
  181. package/packages/server/dist/unread.js +123 -0
  182. package/packages/server/dist/usage.d.ts +66 -0
  183. package/packages/server/dist/usage.js +243 -0
  184. package/packages/server/dist/wake-injection.d.ts +156 -0
  185. package/packages/server/dist/wake-injection.js +136 -0
  186. package/packages/server/dist/whisper-local.d.ts +9 -0
  187. package/packages/server/dist/whisper-local.js +122 -0
  188. package/packages/server/dist/ws-context.d.ts +158 -0
  189. package/packages/server/dist/ws-context.js +1 -0
  190. package/packages/server/dist/ws-protocol.d.ts +423 -0
  191. package/packages/server/dist/ws-protocol.js +251 -0
  192. package/packages/server/dist/ws-self-mcp.d.ts +52 -0
  193. package/packages/server/dist/ws-self-mcp.js +28 -0
  194. package/packages/server/dist/ws-triggers.d.ts +29 -0
  195. package/packages/server/dist/ws-triggers.js +261 -0
  196. package/packages/server/dist/ws-turn.d.ts +86 -0
  197. package/packages/server/dist/ws-turn.js +751 -0
  198. package/packages/server/dist/ws.d.ts +62 -0
  199. package/packages/server/dist/ws.js +1024 -0
  200. package/packages/server/package.json +6 -0
  201. package/packages/web/dist/assets/InstanceConfigPage-Bypd_quL.js +2 -0
  202. package/packages/web/dist/assets/MarkdownRenderer-Co8w9iuX.js +30 -0
  203. package/packages/web/dist/assets/ProjectRedirect-DXrA24Cy.js +2 -0
  204. package/packages/web/dist/assets/ProjectView-B9opP7Sv.js +13 -0
  205. package/packages/web/dist/assets/ProjectsGrid-D2cwb2jY.js +2 -0
  206. package/packages/web/dist/assets/arc-B--FIRB8.js +2 -0
  207. package/packages/web/dist/assets/architectureDiagram-3BPJPVTR-B6dRWZZ6.js +37 -0
  208. package/packages/web/dist/assets/bash-I8pq0VWm.js +2 -0
  209. package/packages/web/dist/assets/blockDiagram-GPEHLZMM-DLxFAvvf.js +133 -0
  210. package/packages/web/dist/assets/c-D2F1sQUL.js +2 -0
  211. package/packages/web/dist/assets/c4Diagram-AAUBKEIU-CNee5ks4.js +11 -0
  212. package/packages/web/dist/assets/channel-Bw0A52CB.js +2 -0
  213. package/packages/web/dist/assets/chunk-2J33WTMH-DObw03Xl.js +2 -0
  214. package/packages/web/dist/assets/chunk-4BX2VUAB-CH2SR6nN.js +2 -0
  215. package/packages/web/dist/assets/chunk-55IACEB6-WlVpegT3.js +2 -0
  216. package/packages/web/dist/assets/chunk-727SXJPM-g8GT9Rgd.js +207 -0
  217. package/packages/web/dist/assets/chunk-AQP2D5EJ-DbN4PsDF.js +232 -0
  218. package/packages/web/dist/assets/chunk-FMBD7UC4-DSN3BjFG.js +16 -0
  219. package/packages/web/dist/assets/chunk-ND2GUHAM-D9R4le0z.js +2 -0
  220. package/packages/web/dist/assets/chunk-QZHKN3VN-D4YgRaKH.js +2 -0
  221. package/packages/web/dist/assets/classDiagram-4FO5ZUOK-itf_kt-q.js +2 -0
  222. package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-itf_kt-q.js +2 -0
  223. package/packages/web/dist/assets/core-CysJ2gLj.js +4 -0
  224. package/packages/web/dist/assets/cose-bilkent-S5V4N54A-D1PB0KNu.js +2 -0
  225. package/packages/web/dist/assets/cpp-Bm3ii6kY.js +2 -0
  226. package/packages/web/dist/assets/csharp-BNpuYZMG.js +2 -0
  227. package/packages/web/dist/assets/css-DazXZka4.js +2 -0
  228. package/packages/web/dist/assets/cytoscape.esm-CUqq0XTU.js +332 -0
  229. package/packages/web/dist/assets/dagre-BM42HDAG-C6Uuq-dq.js +5 -0
  230. package/packages/web/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  231. package/packages/web/dist/assets/diagram-2AECGRRQ-C9JvQ425.js +44 -0
  232. package/packages/web/dist/assets/diagram-5GNKFQAL-Cu6pdh2d.js +11 -0
  233. package/packages/web/dist/assets/diagram-KO2AKTUF-PmXcUQhY.js +4 -0
  234. package/packages/web/dist/assets/diagram-LMA3HP47-9ww6_9Iq.js +25 -0
  235. package/packages/web/dist/assets/diagram-OG6HWLK6-DWQVjv_n.js +25 -0
  236. package/packages/web/dist/assets/erDiagram-TEJ5UH35-6F7xTuff.js +86 -0
  237. package/packages/web/dist/assets/flowDiagram-I6XJVG4X-SCiGu7m6.js +163 -0
  238. package/packages/web/dist/assets/format-yf7Vf9kR.js +2 -0
  239. package/packages/web/dist/assets/ganttDiagram-6RSMTGT7-BNpPwxkL.js +293 -0
  240. package/packages/web/dist/assets/gitGraphDiagram-PVQCEYII-C0gyAJjW.js +107 -0
  241. package/packages/web/dist/assets/go-BPx_y7j8.js +2 -0
  242. package/packages/web/dist/assets/graph-BlwPajkw.js +2 -0
  243. package/packages/web/dist/assets/index-B-YuzkF6.js +71 -0
  244. package/packages/web/dist/assets/index-CvGiQddO.css +1 -0
  245. package/packages/web/dist/assets/infoDiagram-5YYISTIA-CJ2CFPfy.js +3 -0
  246. package/packages/web/dist/assets/ini-B3BVgEsG.js +2 -0
  247. package/packages/web/dist/assets/init-Gi6I4Gst.js +2 -0
  248. package/packages/web/dist/assets/ishikawaDiagram-YF4QCWOH-Cs0D32Ry.js +71 -0
  249. package/packages/web/dist/assets/java-B5VKqtH2.js +2 -0
  250. package/packages/web/dist/assets/javascript-BKRaQes9.js +2 -0
  251. package/packages/web/dist/assets/journeyDiagram-JHISSGLW-Bc9fuI2i.js +140 -0
  252. package/packages/web/dist/assets/json-DIYVocXf.js +2 -0
  253. package/packages/web/dist/assets/kanban-definition-UN3LZRKU-D_tC7QfC.js +90 -0
  254. package/packages/web/dist/assets/katex-C5jXJg4s.js +258 -0
  255. package/packages/web/dist/assets/kotlin-Cw-eF_bm.js +3 -0
  256. package/packages/web/dist/assets/lastTab-CU1zsF0j.js +2 -0
  257. package/packages/web/dist/assets/layout-Boy8vhSP.js +2 -0
  258. package/packages/web/dist/assets/linear-BzGHkF9K.js +2 -0
  259. package/packages/web/dist/assets/mermaid.core-DlbsYzE-.js +310 -0
  260. package/packages/web/dist/assets/mindmap-definition-RKZ34NQL-Bxeh-ToD.js +97 -0
  261. package/packages/web/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  262. package/packages/web/dist/assets/php-CRdd4dfS.js +3 -0
  263. package/packages/web/dist/assets/pieDiagram-4H26LBE5-CDmilo8S.js +31 -0
  264. package/packages/web/dist/assets/python-kSlZHhJb.js +2 -0
  265. package/packages/web/dist/assets/quadrantDiagram-W4KKPZXB-D14-JyE9.js +8 -0
  266. package/packages/web/dist/assets/queued-LamEU0GH.js +2 -0
  267. package/packages/web/dist/assets/requirementDiagram-4Y6WPE33-CJ1PUGu-.js +85 -0
  268. package/packages/web/dist/assets/ruby-7T4ktxyv.js +2 -0
  269. package/packages/web/dist/assets/rust-HC3MBUhY.js +2 -0
  270. package/packages/web/dist/assets/sankeyDiagram-5OEKKPKP-BkUK73W0.js +41 -0
  271. package/packages/web/dist/assets/scss-DZLA7YXz.js +2 -0
  272. package/packages/web/dist/assets/sequenceDiagram-3UESZ5HK-sbcvsuLp.js +163 -0
  273. package/packages/web/dist/assets/sql-BC88IN-V.js +2 -0
  274. package/packages/web/dist/assets/stateDiagram-AJRCARHV-DV-7BdHK.js +2 -0
  275. package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-BlkGdXKW.js +2 -0
  276. package/packages/web/dist/assets/swift-Dn8kKO_1.js +2 -0
  277. package/packages/web/dist/assets/timeline-definition-PNZ67QCA-C5su21G_.js +121 -0
  278. package/packages/web/dist/assets/typescript-C2FFdlUC.js +2 -0
  279. package/packages/web/dist/assets/vennDiagram-CIIHVFJN-JNkIJAaQ.js +35 -0
  280. package/packages/web/dist/assets/wardley-L42UT6IY-qcz6t2vR.js +174 -0
  281. package/packages/web/dist/assets/wardleyDiagram-YWT4CUSO-CMrOIZ-c.js +79 -0
  282. package/packages/web/dist/assets/xml-BXBhIUeX.js +2 -0
  283. package/packages/web/dist/assets/xychartDiagram-2RQKCTM6-C9q6Oy9u.js +8 -0
  284. package/packages/web/dist/assets/yaml-drsDnIKT.js +2 -0
  285. package/packages/web/dist/favicon.ico +0 -0
  286. package/packages/web/dist/fonts/inter-latin.woff2 +0 -0
  287. package/packages/web/dist/fonts/jetbrains-mono-latin.woff2 +0 -0
  288. package/packages/web/dist/icons/apple-touch-icon.png +0 -0
  289. package/packages/web/dist/icons/favicon-16.png +0 -0
  290. package/packages/web/dist/icons/favicon-32.png +0 -0
  291. package/packages/web/dist/icons/icon-192.png +0 -0
  292. package/packages/web/dist/icons/icon-512.png +0 -0
  293. package/packages/web/dist/icons/maskable-512.png +0 -0
  294. package/packages/web/dist/index.html +56 -0
  295. package/packages/web/dist/manifest.webmanifest +32 -0
  296. package/packages/web/dist/sw.js +139 -0
@@ -0,0 +1,158 @@
1
+ /** Bounds only the wait for a turn's FIRST message (never mid-turn): a stale
2
+ * (not-yet-flushed) residue read must not hang us on a backlog turn that never
3
+ * arrives. Comfortably longer than the CLI's inter-turn gap. */
4
+ const FIRST_MESSAGE_TIMEOUT_MS = 8000;
5
+ /** Overall ceiling so consumption can never hang indefinitely. */
6
+ const DEFAULT_DEADLINE_MS = 180000;
7
+ function isErrorResult(m) {
8
+ return (typeof m.subtype === "string" && m.subtype.startsWith("error")) || m.success === false;
9
+ }
10
+ /**
11
+ * Pump the message stream until the next terminal `result`, invoking `onMessage`
12
+ * per message. Drives a SINGLE in-flight `next()` (threaded via `pending`) so the
13
+ * iterator is never advanced twice concurrently.
14
+ *
15
+ * `firstMessageTimeoutMs` bounds ONLY the wait for this turn's first message; once
16
+ * a message has been received the turn is consumed to its `result` unbounded (a
17
+ * slow turn is never cut off). On a first-message timeout the still-in-flight
18
+ * `pending` is handed straight back to the caller.
19
+ */
20
+ async function pumpToResult(iterator, pending, onMessage, onSessionId, firstMessageTimeoutMs) {
21
+ let awaitingFirst = true;
22
+ // eslint-disable-next-line no-constant-condition
23
+ while (true) {
24
+ let step;
25
+ if (awaitingFirst && firstMessageTimeoutMs !== undefined) {
26
+ const TIMED_OUT = Symbol("first-message-timeout");
27
+ let timer;
28
+ const raced = await Promise.race([
29
+ pending,
30
+ new Promise((resolve) => {
31
+ timer = setTimeout(() => resolve(TIMED_OUT), firstMessageTimeoutMs);
32
+ }),
33
+ ]);
34
+ if (timer)
35
+ clearTimeout(timer);
36
+ if (raced === TIMED_OUT)
37
+ return { pending, result: null, done: false, timedOut: true };
38
+ step = raced;
39
+ }
40
+ else {
41
+ step = await pending;
42
+ }
43
+ if (step.done)
44
+ return { pending: Promise.resolve(step), result: null, done: true, timedOut: false };
45
+ awaitingFirst = false;
46
+ const m = step.value;
47
+ if (m.session_id && onSessionId)
48
+ onSessionId(m.session_id);
49
+ if (onMessage)
50
+ await onMessage(step.value);
51
+ if (m.type === "result") {
52
+ return { pending: iterator.next(), result: m, done: false, timedOut: false };
53
+ }
54
+ pending = iterator.next();
55
+ }
56
+ }
57
+ /**
58
+ * Consume a resumed session's stream to the caller's real turn, first draining any
59
+ * stale async-input backlog that replays ahead of it. See the module doc.
60
+ *
61
+ * The session must already have the caller's prompt seeded (chatSession opens with
62
+ * the prompt; runCommand `send`s the command; the wake path is opened with its
63
+ * prompt by herdctl) — this only governs WHEN we stop consuming, never what runs.
64
+ */
65
+ export async function consumeResumedTurn(session, opts) {
66
+ const iterator = session.messages[Symbol.asyncIterator]();
67
+ let pending = iterator.next();
68
+ let sessionId = null;
69
+ const onSid = (id) => {
70
+ sessionId = id;
71
+ opts.onSessionId?.(id);
72
+ };
73
+ const overallDeadline = Date.now() + (opts.deadlineMs ?? DEFAULT_DEADLINE_MS);
74
+ let success = false;
75
+ let turns = 0;
76
+ while (Date.now() < overallDeadline) {
77
+ // Bound only the wait for the next turn's first message while a backlog may
78
+ // still be queued (residue > 0); wait unbounded when the queue is empty (the
79
+ // running turn is the real one and may be slow).
80
+ const firstMsgTimeout = (await opts.residueProbe()) > 0 ? FIRST_MESSAGE_TIMEOUT_MS : undefined;
81
+ const r = await pumpToResult(iterator, pending, opts.onMessage, onSid, firstMsgTimeout);
82
+ pending = r.pending;
83
+ if (r.done)
84
+ break;
85
+ if (r.timedOut)
86
+ break; // no further turn arrived → the queue is effectively drained
87
+ if (r.result) {
88
+ turns++;
89
+ success = !isErrorResult(r.result);
90
+ }
91
+ if ((await opts.residueProbe()) === 0)
92
+ break; // async queue drained → real turn done
93
+ }
94
+ if (turns > 1)
95
+ opts.log?.(`drained ${turns - 1} backlog turn(s) ahead of the real turn`);
96
+ return { success, sessionId, iterator, pending };
97
+ }
98
+ /**
99
+ * Gap B — deliver autonomous background-completion turns.
100
+ *
101
+ * After {@link consumeResumedTurn} returns, the primary turn is done but the
102
+ * session may be held OPEN by the reaper because the turn launched continuous
103
+ * background work (`decideReap` keepAlives while `backgroundTasks.length > 0`).
104
+ * The `claude` subprocess keeps running and, when a background task completes,
105
+ * the SDK hands the parent the result as a fresh autonomous re-invocation turn —
106
+ * emitted on the SAME message stream. Paddock used to stop consuming at the
107
+ * primary `result`, so those messages buffered undelivered: the CLI still
108
+ * PERSISTED the re-invocation to the transcript (the subprocess writes JSONL
109
+ * directly), but the live UI never saw it until a refresh replayed history.
110
+ *
111
+ * This keeps consuming the handed-back {@link ConsumeResumedTurnResult.iterator}
112
+ * (never a second iterator — that would split the stream), forwarding every
113
+ * subsequent message to `onMessage`. The caller's `onMessage` renders each
114
+ * autonomous turn to the hub exactly like the scheduler-wake path does, so it
115
+ * appears live. Consumption ALSO keeps `tapLifecycleStream` advancing, so the
116
+ * reaper keeps receiving its mid-turn `activity` / `background_tasks_changed`
117
+ * signals (they fire as a side effect of the consumer pulling messages) — the
118
+ * behaviour the reaper was designed around.
119
+ *
120
+ * The loop ends cleanly when the reaper reaps the idle session: `close()` ends
121
+ * the SDK query, the iterator yields `done`, and we return. A stream error is
122
+ * swallowed (the turn is already over). Detached by the caller (`void`), it
123
+ * never throws to the event loop.
124
+ */
125
+ export async function consumeBackgroundTurns(iterator, pending, onMessage, onDone) {
126
+ try {
127
+ // eslint-disable-next-line no-constant-condition
128
+ while (true) {
129
+ let step;
130
+ try {
131
+ step = await pending;
132
+ }
133
+ catch {
134
+ return; // stream errored/closed — nothing more to deliver
135
+ }
136
+ if (step.done)
137
+ return; // reaper reaped the session → stream ended
138
+ try {
139
+ await onMessage(step.value);
140
+ }
141
+ catch {
142
+ /* a rendering failure for one message must not abort delivery */
143
+ }
144
+ pending = iterator.next();
145
+ }
146
+ }
147
+ finally {
148
+ // Signal the caller (the background sink) that the stream has ended so it can
149
+ // finalize its single hub turn (emit chat:complete + turn.end()). Runs on
150
+ // normal end, error, or reap. Never throws to the event loop.
151
+ try {
152
+ await onDone?.();
153
+ }
154
+ catch {
155
+ /* completion signalling must not throw to a detached consumer */
156
+ }
157
+ }
158
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * The route dependency bag + the shared per-request context the route groups
3
+ * close over.
4
+ *
5
+ * `RouteDeps` is the raw service bag `app.ts` hands to {@link registerRoutes}.
6
+ * `buildRouteContext` constructs — ONCE — the handful of helper closures that the
7
+ * original single `registerRoutes` defined inline (read-state user resolution,
8
+ * slug→agent/dir resolution, attachment cleanup, the per-chat usage resolver) and
9
+ * returns them alongside every dep as a {@link RouteCtx}. Each `registerXRoutes`
10
+ * group then takes that context instead of relying on closure scope — which is
11
+ * what lets the ~50 handlers live in per-group modules while sharing one wiring.
12
+ */
13
+ import type { FastifyRequest } from "fastify";
14
+ import type { DiscoveredSession } from "@herdctl/core";
15
+ import type { ProjectStore } from "./projects.js";
16
+ import { AttachmentStore } from "./attachments.js";
17
+ import type { HerdctlService } from "./herdctl.js";
18
+ import type { GitService } from "./git.js";
19
+ import type { GithubAuth } from "./github-auth.js";
20
+ import type { ArchiveStore } from "./archive.js";
21
+ import type { StarStore } from "./star.js";
22
+ import type { ReadStateStore } from "./read-state.js";
23
+ import type { UnreadStore } from "./unread.js";
24
+ import type { ParentDetachStore } from "./parent-detach.js";
25
+ import type { RunProvenanceStore } from "./run-provenance.js";
26
+ import type { MessageProvenanceStore } from "./message-provenance.js";
27
+ import type { PaddockConfig } from "./config.js";
28
+ import { type Transcriber } from "./transcribe.js";
29
+ import type { PaddockEventBus } from "./event-bus.js";
30
+ import type { ChatHandlerContext } from "./ws-context.js";
31
+ import type { TriggerService } from "./triggers.js";
32
+ import { type ChatUsage } from "./chat-dto.js";
33
+ /**
34
+ * The subset of @fastify/multipart's decorated request we use. The plugin
35
+ * decorates `req.file()` at runtime; we model just what we need here rather than
36
+ * rely on the plugin's global `declare module 'fastify'` augmentation, which is
37
+ * brittle under workspace hoisting (fastify can resolve to a different physical
38
+ * copy than the one the plugin augments).
39
+ */
40
+ export interface UploadedFile {
41
+ filename?: string;
42
+ mimetype?: string;
43
+ toBuffer(): Promise<Buffer>;
44
+ }
45
+ /** A streamed multipart part (issue #328 upload): a file or a form field. */
46
+ export interface MultipartPart {
47
+ type: "file" | "field";
48
+ filename?: string;
49
+ mimetype?: string;
50
+ toBuffer(): Promise<Buffer>;
51
+ }
52
+ export interface MultipartLimits {
53
+ limits?: {
54
+ fileSize?: number;
55
+ files?: number;
56
+ };
57
+ }
58
+ export type MultipartRequest = FastifyRequest & {
59
+ file(): Promise<UploadedFile | undefined>;
60
+ parts(opts?: MultipartLimits): AsyncIterableIterator<MultipartPart>;
61
+ };
62
+ export interface RouteDeps {
63
+ projects: ProjectStore;
64
+ herdctl: HerdctlService;
65
+ git: GitService;
66
+ githubAuth: GithubAuth;
67
+ transcriber: Transcriber;
68
+ archive: ArchiveStore;
69
+ /** Per-chat starred/pinned-flag sidecar (#373). Orthogonal to `archive`. */
70
+ star: StarStore;
71
+ readState: ReadStateStore;
72
+ /**
73
+ * Per-user MANUAL "unread" override sidecar (#458): an explicit "mark unread"
74
+ * flag layered on top of the derived read-state, so a chat can resurface its
75
+ * unread cue even after its last turn was seen. Cleared when the chat is marked
76
+ * seen (opened / focused / marked read).
77
+ */
78
+ unread: UnreadStore;
79
+ /**
80
+ * Explicit "this chat was detached from its parent" sidecar (#508). Consulted
81
+ * by `makeParentResolver` AHEAD of the recorded and inferred parent tiers, so a
82
+ * detach can't be undone by the inference that re-derives an unrecorded edge.
83
+ */
84
+ parentDetach: ParentDetachStore;
85
+ runProvenance: RunProvenanceStore;
86
+ /**
87
+ * Per-MESSAGE provenance sidecar (issue #290): who injected each machine-added
88
+ * turn. Joined into a chat's message DTO so the history can attribute injected
89
+ * turns ("↩ sent by …" / "⏰ scheduled by …"). Absence ⇒ human (the default).
90
+ */
91
+ messageProvenance: MessageProvenanceStore;
92
+ attachments: AttachmentStore;
93
+ /**
94
+ * Manually fire a project's TRIGGER now (Epic T follow-up / #327), backing the
95
+ * `POST …/triggers/:name/run` "Run now" route + the `run_trigger` self-MCP verb.
96
+ * Supplied by the chat handler (`makeChatHandler(...).fireTrigger`) so a manual run
97
+ * goes through the SAME hub path a cron / event fire uses — a first-class, badged
98
+ * chat, indistinguishable from an automatic fire. Fires ANY trigger type regardless
99
+ * of its `enabled` flag. Resolves the started chat's session id, or `null` if the
100
+ * project/trigger is gone or nothing started. Optional so tests that don't exercise
101
+ * triggers can omit it.
102
+ */
103
+ fireTrigger?: (slug: string, triggerName: string) => Promise<string | null>;
104
+ /**
105
+ * The shared Management API ops context (issue #312 M1) — the SAME bag the
106
+ * in-process self-MCP is built from (`makeChatHandler(...).managementOpsContext`).
107
+ * Handed to the route layer so a non-socket caller can build the management ops
108
+ * for itself via `buildManagementOps` + `enforceManagementPolicy`: the `/mcp`
109
+ * transport in M2, and the turn-spawning REST endpoints in #465, which is why
110
+ * this is threaded now rather than when the transport lands. Optional so tests
111
+ * that don't exercise the management API can omit it.
112
+ */
113
+ managementOpsContext?: ChatHandlerContext;
114
+ /**
115
+ * In-process lifecycle event bus (Epic T). The archive route emits `onArchive`
116
+ * on it AFTER the archive commits, so the trigger dispatcher (wired in the chat
117
+ * handler) fires the project's enabled onArchive event triggers. Optional so tests
118
+ * that don't exercise triggers can omit it.
119
+ */
120
+ events?: PaddockEventBus;
121
+ /**
122
+ * Unified trigger CRUD service (Epic T "Unify Triggers" / T3). Backs the
123
+ * `/api/projects/:slug/triggers[/:name]` REST surface the Triggers tab (T4) will
124
+ * drive — list (with the capability-picker catalog), create/replace, edit,
125
+ * enable/disable (just `set` with `enabled` flipped — GG-3), delete. Delegates
126
+ * persistence to `project.yaml`'s single `triggers` block + herdctl arming to
127
+ * {@link TriggerService}. Optional so tests that don't exercise triggers can omit it.
128
+ */
129
+ triggers?: TriggerService;
130
+ cfg: PaddockConfig;
131
+ }
132
+ /**
133
+ * `RouteDeps` plus the shared helper closures built once by
134
+ * {@link buildRouteContext}. This is what every `registerXRoutes(app, ctx)` group
135
+ * receives.
136
+ */
137
+ export interface RouteCtx extends RouteDeps {
138
+ /**
139
+ * Resolve the read-state key's user segment from the authenticated principal:
140
+ * a REAL identity (trusted-header / jwt) keys read-state by username; an
141
+ * anonymous principal (`none` mode) uses the shared bucket (null → no user
142
+ * segment). This is the ONLY place user identity is consumed — read-state is
143
+ * user-keyed-when-present; chat VISIBILITY is deliberately not gated (#189).
144
+ */
145
+ readStateUser(req: FastifyRequest): string | null;
146
+ /** Resolve a slug to the agent name whose sessions back it. */
147
+ agentForSlug(slug: string): Promise<string>;
148
+ /** Resolve a slug to the on-disk project directory holding its `.chats/`. */
149
+ projectDirForSlug(slug: string): Promise<string>;
150
+ /**
151
+ * Remove the attachments a chat referenced, before its transcript is deleted
152
+ * (we read the transcript to find the ids). Best-effort — a failure here must
153
+ * never block the chat delete.
154
+ */
155
+ cleanupAttachments(agent: string, sessionId: string): Promise<void>;
156
+ /**
157
+ * A per-session usage lookup for building a chat list's usage rings (issue
158
+ * #77) + cumulative token/cost figures (issue #152, incl. sub-agents #242):
159
+ * reads each session's transcript plus its sub-agent transcripts (both memoized
160
+ * on mtime) and pairs the parsed totals with the model. Returns null for a
161
+ * session with no usage data yet — the ring simply hides. Keyed on `projectDir`
162
+ * (not the agent name) because paddock resolves transcripts directly under
163
+ * `<projectDir>/.chats/`.
164
+ */
165
+ chatUsageResolver(projectDir: string, model: string): (s: DiscoveredSession) => Promise<ChatUsage | null>;
166
+ }
167
+ /** Construct the shared route context (deps + the helper closures) once. */
168
+ export declare function buildRouteContext(deps: RouteDeps): RouteCtx;
@@ -0,0 +1,39 @@
1
+ import { collectAttachmentIds } from "./attachments.js";
2
+ import { keeperAgentName } from "./herdctl.js";
3
+ import { readSessionTokenUsageWithSubagents } from "./subagents.js";
4
+ import { toChatUsage } from "./chat-dto.js";
5
+ /** Construct the shared route context (deps + the helper closures) once. */
6
+ export function buildRouteContext(deps) {
7
+ const { projects, herdctl, attachments } = deps;
8
+ const readStateUser = (req) => req.user && !req.user.anonymous ? req.user.username : null;
9
+ async function agentForSlug(slug) {
10
+ await projects.get(slug); // throws not_found for unknown slug
11
+ return keeperAgentName(slug);
12
+ }
13
+ async function projectDirForSlug(slug) {
14
+ return (await projects.get(slug)).dir;
15
+ }
16
+ async function cleanupAttachments(agent, sessionId) {
17
+ try {
18
+ const messages = await herdctl.sessionMessages(agent, sessionId);
19
+ await attachments.remove(collectAttachmentIds(messages));
20
+ }
21
+ catch {
22
+ /* best-effort: orphaned attachment files are harmless */
23
+ }
24
+ }
25
+ function chatUsageResolver(projectDir, model) {
26
+ return async (s) => {
27
+ const u = await readSessionTokenUsageWithSubagents(projectDir, s.sessionId).catch(() => null);
28
+ return u ? toChatUsage(u, model) : null;
29
+ };
30
+ }
31
+ return {
32
+ ...deps,
33
+ readStateUser,
34
+ agentForSlug,
35
+ projectDirForSlug,
36
+ cleanupAttachments,
37
+ chatUsageResolver,
38
+ };
39
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Shared REST error helper. `sendProjectError` maps a {@link ProjectError} onto
3
+ * the right HTTP status (404 not_found / 409 exists|not_directory / 400 else) and
4
+ * falls back to a logged 500 for anything unexpected. Used by nearly every
5
+ * handler, so it lands in its own module and is imported everywhere.
6
+ */
7
+ import type { FastifyReply } from "fastify";
8
+ export declare function sendProjectError(reply: FastifyReply, err: unknown): FastifyReply<import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").RouteGenericInterface, unknown, import("fastify").FastifySchema, import("fastify").FastifyTypeProviderDefault, unknown>;
@@ -0,0 +1,13 @@
1
+ import { ProjectError } from "./projects.js";
2
+ export function sendProjectError(reply, err) {
3
+ if (err instanceof ProjectError) {
4
+ const code = err.code === "not_found"
5
+ ? 404
6
+ : err.code === "exists" || err.code === "not_directory"
7
+ ? 409
8
+ : 400;
9
+ return reply.code(code).send({ error: err.message, code: err.code });
10
+ }
11
+ reply.log.error({ err }, "route error");
12
+ return reply.code(500).send({ error: err.message ?? "internal error" });
13
+ }
@@ -0,0 +1,12 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type { RouteCtx } from "../route-context.js";
3
+ /**
4
+ * Workspace-scoped chat routes: paths are declared RELATIVE to the workspace
5
+ * (e.g. `/chats/:sessionId`), and this plugin is mounted TWICE — once at
6
+ * `/api/root` and once at `/api/projects/:slug` (see `workspace-mount.ts`).
7
+ *
8
+ * Handlers still read `req.params.slug`; the root mount injects `slug: ""` in an
9
+ * `onRequest` hook, which runs before params validation, so the `required:
10
+ * ["slug"]` schemas below keep validating unchanged on both mounts.
11
+ */
12
+ export declare function registerChatWorkspaceRoutes(app: FastifyInstance, ctx: RouteCtx): void;