@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,623 @@
1
+ /**
2
+ * HerdctlService — REAL wrapper around @herdctl/core's FleetManager.
3
+ *
4
+ * This module is NOT a stub: it imports and constructs the public
5
+ * @herdctl/core API (paddock depends on ^5.27.0). It is the single seam between
6
+ * paddock and herdctl, so that any remaining gaps in the public API are isolated
7
+ * here. See docs/INTEGRATION.md for the verified call-by-call contract.
8
+ *
9
+ * As of @herdctl/core 5.11.0 the four prior app-layer workarounds are GONE —
10
+ * paddock now uses the first-class APIs:
11
+ *
12
+ * - **Runtime agents:** `fleet.addAgent({...})` / `fleet.removeAgent(name)`
13
+ * register/unregister a project's keeper + sweeper agents in memory. No more
14
+ * generate-per-agent-yaml + regenerate-herdctl.yaml + `reload()` dance. The
15
+ * FleetManager boots from a minimal zero-agent config (fleet + defaults only)
16
+ * and every agent is added programmatically. `addAgent` validates the config,
17
+ * deep-merges fleet `defaults`, resolves the working_directory to an absolute
18
+ * path, and wires it into the scheduler so it is immediately triggerable and
19
+ * shows up in `getFleetStatus()` / `getAgentInfo()`.
20
+ *
21
+ * - **Sessions:** `fleet.getAgentSessions(name, {limit})` and
22
+ * `fleet.getAgentSessionMessages(name, sessionId)` derive the agent's
23
+ * working_directory + Docker mode from the loaded config — no hand-rolled
24
+ * SessionDiscoveryService keyed on working dirs.
25
+ *
26
+ * - **Streaming:** `trigger(agent, schedule?, { onMessage, prompt, resume })`.
27
+ * `onMessage(msg: SDKMessage)` fires per SDK message; the returned
28
+ * TriggerResult carries the final `sessionId`. New chat = `resume: null`;
29
+ * resume = `resume: <sessionId>`. This is the `batch` drive-mode path; the
30
+ * default `session` mode instead drives `openChatSession`, which ALWAYS runs
31
+ * the SDK runtime regardless of the agent's `runtime` field (see #585).
32
+ *
33
+ * - **Delete / rename a chat:** `fleet.deleteSession(name, sessionId)` removes
34
+ * the transcript (and invalidates the discovery cache), and
35
+ * `fleet.setSessionName(name, sessionId, customName)` sets a custom name —
36
+ * both keyed by agent name, no deep imports.
37
+ *
38
+ * Freshness note: `fleet.getAgentSessions` uses the FleetManager's internal
39
+ * SessionDiscoveryService, which has a 30s directory cache. A brand-new
40
+ * project's transcript dir does not exist until its first turn, and the
41
+ * discovery service does NOT cache a missing directory — so the first session
42
+ * of a project surfaces immediately. `deleteSession`/`setSessionName` invalidate
43
+ * the cache internally. The one nuance vs. the prior code: a *second* new chat
44
+ * created in an already-listed project within 30s may take up to the cache TTL
45
+ * to appear (the prior code invalidated a private cache we can no longer reach).
46
+ * That last nuance is now closed too: core exposes `fleet.invalidateSessions(name)`
47
+ * and paddock calls it after each turn (`ws-turn.ts`) and around fork/promote.
48
+ */
49
+ import { FleetManager, type DiscoveredSession, type ChatMessage, type SDKMessage, type TriggerResult, type AgentInfo, type FleetStatus, type SlashCommand, type InjectedMcpServerDef, type SessionWakeHandler, type ResolveInjectedMcpServers, type ScheduleTriggerHandler, type ScheduleInfo, type JobMetadata, type AdoptionPlacementMode, type AdoptSkippedSession } from "@herdctl/core";
50
+ import type { PaddockConfig } from "./config.js";
51
+ import type { Project } from "./projects.js";
52
+ import { type PaddockTrigger } from "./trigger-config.js";
53
+ import { type AdoptableSummary, type FilterReason } from "./adoptable.js";
54
+ /**
55
+ * One session an import did NOT bring in, with a reason a UI can show verbatim.
56
+ *
57
+ * A superset of the engine's own `AdoptSkippedSession`: paddock's noise filter
58
+ * (`./adoptable.ts`) contributes its own reasons on top of the engine's, and a
59
+ * caller renders the string either way rather than switching on it.
60
+ */
61
+ export type AdoptSkipped = AdoptSkippedSession | {
62
+ sessionId: string;
63
+ reason: FilterReason;
64
+ detail?: string;
65
+ };
66
+ /** Options passed through to a streamed trigger. */
67
+ export interface ChatTurnOptions {
68
+ prompt: string;
69
+ /** Session to resume; `null` forces a fresh session; omit for agent fallback. */
70
+ resume?: string | null;
71
+ onMessage?: (msg: SDKMessage) => void | Promise<void>;
72
+ onJobCreated?: (jobId: string) => void;
73
+ triggerType?: string;
74
+ /**
75
+ * In-process MCP servers to inject for this turn (issue #112). herdctl's CLI
76
+ * runtime stands up a localhost HTTP bridge per server and auto-allowlists its
77
+ * `mcp__<key>__*` tools, so no change to the static `allowed_tools` is needed.
78
+ */
79
+ injectedMcpServers?: Record<string, InjectedMcpServerDef>;
80
+ /**
81
+ * Gap B (session mode only): a sink for messages that arrive on the session's
82
+ * stream AFTER the primary turn's terminal `result` — i.e. autonomous
83
+ * background-completion re-invocation turns the reaper holds the session open
84
+ * for. When provided, {@link chatSession} keeps consuming the same stream and
85
+ * forwards each subsequent message here so the caller can render it live (see
86
+ * `consumeBackgroundTurns` in ./resume-drain.ts). Omit for no live delivery of
87
+ * background turns (they still persist to the transcript regardless).
88
+ */
89
+ onBackgroundMessage?: (msg: SDKMessage) => void | Promise<void>;
90
+ /**
91
+ * Gap B (session mode only): called once the background stream ENDS (the reaper
92
+ * reaped the session). Lets the caller finalize the single hub turn it rendered
93
+ * background re-invocations onto (emit chat:complete + end the turn) so the
94
+ * streaming indicator clears. Paired with {@link onBackgroundMessage}.
95
+ */
96
+ onBackgroundDone?: () => void | Promise<void>;
97
+ }
98
+ /**
99
+ * The name/visibility helpers + tool/model constants are pure (they only read
100
+ * their arguments, no fleet/cfg/session state), so they live in a sibling module
101
+ * for readability + isolated unit-testing (issue #403). Re-exported here so every
102
+ * external importer keeps resolving them via `./herdctl.js` unchanged.
103
+ */
104
+ export { keeperAgentName, keeperSlugFromAgent, SWEEPER_PREFIX, sweeperAgentName, HOOK_AGENT_PREFIX, hookAgentName, TRIGGER_AGENT_PREFIX, triggerAgentName, visibleProjectAgentNames, BROWSER_MCP_TOOL, DENIED_TOOLS, browserMcpServers, KEEPER_MAX_CONCURRENT, KEEPER_SESSION_TIMEOUT, } from "./herdctl-agent-names.js";
105
+ export declare function sliceTranscriptAtUuid(raw: string, cutUuid: string, endOnCompletedAssistant?: boolean): string | null;
106
+ export declare class HerdctlService {
107
+ private readonly cfg;
108
+ private fleet;
109
+ private started;
110
+ /**
111
+ * The model currently registered for each agent (keyed by agent name). Lets
112
+ * `ensureAgentModel` skips a re-registration when the
113
+ * requested model already matches the live agent config.
114
+ *
115
+ * SINGLE-USER CAVEAT: the keeper is one shared agent per project, so the
116
+ * model is last-write-wins across concurrent chats of the same project — if
117
+ * two chats of the same project pick different models, whichever triggered
118
+ * last wins for both. Acceptable for paddock's single-user POC; a clean
119
+ * per-trigger model override is a herdctl follow-up.
120
+ */
121
+ private agentModels;
122
+ /**
123
+ * Each agent's working directory (the keeper's cwd), recorded at
124
+ * registration. Used to resolve the `claude` CLI transcript for a resume so we
125
+ * can measure its pending async-input-queue depth (the resume self-interrupt
126
+ * fix's residue gate — see {@link residueDepthFor} + `./resume-drain.ts`).
127
+ */
128
+ private agentWorkingDirs;
129
+ /**
130
+ * Live session-mode turns keyed by the synthetic turn id we hand back as the
131
+ * `jobId` (Paddock#111). `openChatSession` creates no herdctl job record, so
132
+ * there's no core `jobId` to cancel via `cancelJob`; instead we register the
133
+ * live {@link RuntimeSession} here and Stop → `session.interrupt()` (see
134
+ * {@link cancel}). Entries are removed when the turn's stream ends.
135
+ */
136
+ private liveSessions;
137
+ /**
138
+ * Incremental index of `<stateDir>/jobs`, backing the unread-badge reads
139
+ * (`lastTurnCompletedAt*`) — one per service, reused across requests so a warm
140
+ * scan only parses records it has never seen (#529). See
141
+ * `./herdctl-jobs-index.ts` for the invariant it rests on and its invalidation.
142
+ *
143
+ * Built lazily on first read, like the job-record helpers it replaced: unit
144
+ * tests construct this service against a partial config with no `stateDir`,
145
+ * and must keep being able to.
146
+ */
147
+ private jobsIndexOrNull;
148
+ private get jobsIndex();
149
+ constructor(cfg: PaddockConfig);
150
+ /**
151
+ * The environment system prompt to append to a keeper turn (issue #635), or
152
+ * `undefined` when the instance opted out (`environmentPrompt: ""`) — herdctl
153
+ * treats a falsy `systemPromptAppend` as "don't touch the system prompt", so
154
+ * an opted-out instance is byte-identical to pre-#635 behaviour.
155
+ *
156
+ * Read from `this.cfg` here rather than taken as a per-turn option **on
157
+ * purpose**. Every user-visible turn shape — a human send, a slash command, a
158
+ * scheduled wake, a spawned sub-chat, a `driveMode: batch` trigger — funnels
159
+ * through {@link chat}, {@link chatSession} or {@link runCommand}, so applying
160
+ * it at that choke point makes coverage structural. Threading it through
161
+ * `ChatTurnOptions` instead would make it one more thing five call sites have
162
+ * to remember, which is exactly how `isSidechainMessage` ended up wired into
163
+ * 1 of 5 turn paths.
164
+ *
165
+ * It is orthogonal to `nativeSystemPrompt`: herdctl appends to whatever prompt
166
+ * the agent has, so this rides on top of the native preset AND of Paddock's
167
+ * terse replace prompt (see `buildSystemPrompt` in core's sdk-runtime, which
168
+ * folds an append into a preset's `append` field or concatenates onto a
169
+ * literal string).
170
+ */
171
+ private get environmentPromptAppend();
172
+ /**
173
+ * The same value for the **CLI** runtime (`driveMode: batch`), where it is NOT
174
+ * always safe to send.
175
+ *
176
+ * herdctl's CLI runtime has no `--append-system-prompt`; it folds
177
+ * `systemPromptAppend` into the single `--system-prompt` flag
178
+ * (`cli-runtime.ts`), and `--system-prompt` REPLACES Claude Code's preset:
179
+ *
180
+ * agent.system_prompt && append → --system-prompt "<agent>\n\n<append>" ✅ appends
181
+ * append only → --system-prompt "<append>" ❌ REPLACES
182
+ *
183
+ * Paddock sets `agent.system_prompt` only when `nativeSystemPrompt` is false
184
+ * (herdctl-agent-config.ts), so on a DEFAULT batch instance the second branch
185
+ * fires and a two-rule environment note would silently become the agent's
186
+ * entire system prompt — the whole coding preset gone. That is the same
187
+ * replace-vs-append trap #635 warns about, one layer down.
188
+ *
189
+ * So: on the CLI path we send the append only when it will genuinely be
190
+ * appended. A default (native) batch instance is left byte-identical to
191
+ * pre-#635 and simply doesn't get the environment prompt — a missing hint is
192
+ * survivable; a missing coding prompt is not. The SDK runtime has no such
193
+ * problem (it folds the text into the preset's `append` field), and the SDK
194
+ * runtime is what every chat actually uses: `openChatSession` hard-codes it,
195
+ * and `session` is the default drive mode. Batch is the legacy path.
196
+ *
197
+ * Lifting this needs `--append-system-prompt` support in core's CLI runtime;
198
+ * tracked upstream in edspencer/herdctl.
199
+ */
200
+ private get environmentPromptAppendForCli();
201
+ /**
202
+ * Construct + initialize the FleetManager against a minimal zero-agent
203
+ * config (fleet + defaults only). Agents are then registered programmatically
204
+ * via `fleet.addAgent(...)` — a keeper + sweeper for each workspace, the root
205
+ * included. No per-agent yaml files; no `reload()`.
206
+ */
207
+ init(projects: Project[]): Promise<void>;
208
+ /** Start the scheduler (keeper agents currently have no schedules, but this
209
+ * keeps the fleet "running" for status + future cron curators). */
210
+ start(): Promise<void>;
211
+ stop(): Promise<void>;
212
+ /** Expose the raw FleetManager (events, advanced ops) to callers that need it. */
213
+ get manager(): FleetManager;
214
+ /**
215
+ * Is a `claude` SUBPROCESS still alive on this session at the SDK/reaper layer
216
+ * (issue #397)? Consults herdctl's `SessionReaper` via the fleet's session
217
+ * lifecycle manager: `true` while the reaper is keeping a session open for a
218
+ * killed background task (keepAlive) or holding it through its re-invocation
219
+ * grace. Paddock's recovery engine treats this as a busy signal so it never
220
+ * fires a COMPETING resume into a session a prior subprocess still holds live
221
+ * (which the SDK resolves by interrupting the in-flight turn). Null-safe: with
222
+ * no fleet, no session-lifecycle manager (batch mode / reaper disabled), or any
223
+ * error, returns `false` — the pre-#397 behaviour.
224
+ */
225
+ isSdkSessionLive(sessionId: string): boolean;
226
+ fleetStatus(): Promise<FleetStatus>;
227
+ agents(): Promise<AgentInfo[]>;
228
+ /**
229
+ * Register (or replace) a project's keeper + sweeper agents at runtime. Uses
230
+ * `fleet.addAgent` (replace: true), so a re-create or rename of the same slug
231
+ * is idempotent. The new agents are immediately triggerable and visible in
232
+ * fleet status — no yaml + reload round-trip.
233
+ */
234
+ ensureProjectAgent(project: Project): Promise<void>;
235
+ /**
236
+ * Register (or replace) every trigger a project declares that runs on its OWN scoped
237
+ * agent `trigger-<slug>-<name>` (Epic T). Idempotent (`addAgent` replace:true). Which triggers
238
+ * get a scoped agent is decided by {@link triggerRunsOnOwnAgent}: EVENT triggers
239
+ * always; SCHEDULE triggers only when they declare a non-empty `run.tools` allow-list
240
+ * (T2 — an unscoped schedule still rides the keeper's forwarded `schedules` block and
241
+ * runs as the keeper); webhook triggers are reserved (no ingress). A scoped schedule
242
+ * ALSO keeps its keeper `schedules` entry — that is only the cron TIMING; the fired
243
+ * turn executes on this scoped agent. A trigger-less project is a no-op. Called at
244
+ * boot ({@link init}) and on every {@link ensureProjectAgent}.
245
+ */
246
+ registerTriggerAgents(project: Project): Promise<void>;
247
+ /**
248
+ * Register (or replace) ONE trigger's scoped agent at runtime (Epic T) — the herdctl
249
+ * half of a trigger mutation (the Paddock half persists it via
250
+ * `ProjectStore.setTrigger`). Idempotent; immediately fireable. Consumed by
251
+ * {@link import("./triggers.js").TriggerService}. A no-op for a trigger that does NOT
252
+ * run on its own agent ({@link triggerRunsOnOwnAgent}) — an unscoped schedule arms via
253
+ * the keeper re-register; a webhook is reserved.
254
+ */
255
+ ensureTriggerAgent(project: Project, name: string, trigger: PaddockTrigger): Promise<void>;
256
+ /**
257
+ * Unregister one event trigger's agent at runtime (Epic T / T1) — the inverse of
258
+ * {@link ensureTriggerAgent}. Never throws if the agent is already gone.
259
+ */
260
+ removeTriggerAgent(slug: string, name: string): Promise<void>;
261
+ /**
262
+ * Ensure a project's keeper agent is registered at `model`, re-registering it
263
+ * (addAgent replace:true) only when the model actually changed. Used by the
264
+ * WS chat path to honor a per-chat model override before triggering.
265
+ *
266
+ * No herdctl per-trigger model API exists yet, so a model override is applied
267
+ * by re-registering the (single, shared) keeper agent. See the `agentModels`
268
+ * single-user caveat: this is last-write-wins across concurrent chats of the
269
+ * same project.
270
+ */
271
+ ensureAgentModel(project: Project, model: string): Promise<void>;
272
+ /**
273
+ * Force the FleetManager's session-discovery cache to drop its cached listing
274
+ * for an agent, so a brand-new chat surfaces immediately (rather than waiting
275
+ * out the 30s directory cache). New public API in @herdctl/core 5.12.0. No-op
276
+ * and never throws if the fleet isn't ready.
277
+ */
278
+ invalidateSessions(agentName: string): void;
279
+ /**
280
+ * Detector for a project's importable native Claude Code chats, with its own
281
+ * mtime-keyed cache. Lazy for the same reason {@link jobsIndex} is: unit tests
282
+ * construct this service against a partial config.
283
+ */
284
+ private adoptableIndexOrNull;
285
+ private get adoptableIndex();
286
+ /** What `project` could import right now (see `./adoptable.ts`). */
287
+ listAdoptable(project: Project): Promise<AdoptableSummary>;
288
+ /**
289
+ * Drop the cached adoptable summary for a workspace (or all of them).
290
+ *
291
+ * The workspace key may be `""` (the root), so this forwards the argument
292
+ * as-is — `if (!slug)` here would silently clear the WRONG thing.
293
+ */
294
+ invalidateAdoptable(projectKey?: string): void;
295
+ /**
296
+ * Import a project's native Claude Code chats.
297
+ *
298
+ * With `sourceCwd`, imports that one source; without, every source detection
299
+ * currently offers. `mode` defaults to `copy`, which never mutates the user's
300
+ * original `~/.claude` transcripts and preserves their mtimes; `move` and
301
+ * `link` exist for the CLI and nothing in the web UI should reach them.
302
+ *
303
+ * ## Honouring the noise filter
304
+ *
305
+ * Detection withholds empty and slash-command-only transcripts, but the engine
306
+ * adopts a whole SOURCE — it has no "adopt these ids" call. Left alone, that
307
+ * means the header offers 2 chats and the import brings in 4, including a
308
+ * zero-byte stub. So anything the engine adopted that detection had withheld is
309
+ * released again ({@link FleetManager.unadoptSession}) and reported in
310
+ * `skipped` under the filter's own reason, and — in `copy` mode only — the copy
311
+ * this call just placed is removed.
312
+ *
313
+ * That deletion is deliberately narrow. It happens only when all of: the mode
314
+ * is `copy` (in `move` the original is GONE, so the copy is the user's only
315
+ * remaining copy and must never be touched); the engine reported the id as
316
+ * newly `adopted` in THIS call, so it placed the file here and did not find one
317
+ * (`destination-exists` is a skip, not an adopt); and the destination path
318
+ * differs from the source, so an in-place adoption can never delete an
319
+ * original. The result is that a withheld session is left exactly as it was
320
+ * before the import.
321
+ *
322
+ * Afterwards the herdctl session cache, the jobs index and the adoptable cache
323
+ * are all dropped, so the imported chats list on the very next request — the
324
+ * same trio {@link promoteSession} invalidates for the same reason.
325
+ */
326
+ adoptChats(project: Project, opts?: {
327
+ sourceCwd?: string;
328
+ mode?: AdoptionPlacementMode;
329
+ dryRun?: boolean;
330
+ }): Promise<{
331
+ adopted: string[];
332
+ skipped: AdoptSkipped[];
333
+ }>;
334
+ /**
335
+ * Undo the adoption of a session detection had withheld, restoring the state
336
+ * that existed before this import. See {@link adoptChats} for why the file
337
+ * removal is safe and why it is limited to `copy` mode.
338
+ */
339
+ private releaseWithheld;
340
+ /**
341
+ * Unregister a project's keeper + sweeper (+ any event-hook) agents at runtime.
342
+ * Uses `fleet.removeAgent`, the inverse of ensureProjectAgent. Running jobs are
343
+ * unaffected; the scheduler stops triggering the removed agents. `hookNames` are
344
+ * the project's declared hook names (from its DTO) so their `hook-<slug>-<name>`
345
+ * agents are torn down too — caller passes `Object.keys(project.hooks ?? {})`.
346
+ */
347
+ removeProjectAgent(slug: string, hookNames?: string[], triggerNames?: string[]): Promise<void>;
348
+ /**
349
+ * Delete a single chat (session) by agent name + session id. The FleetManager
350
+ * resolves the agent's working directory, removes the transcript JSONL, and
351
+ * invalidates the discovery cache so the list reflects it immediately.
352
+ * Validates the sessionId (rejects path traversal). Returns true if a
353
+ * transcript file was removed, false if none existed.
354
+ */
355
+ deleteSession(agentName: string, sessionId: string): Promise<boolean>;
356
+ /**
357
+ * Set (or clear) a chat's custom display name. Writes through the fleet's
358
+ * shared SessionMetadataStore so a subsequent getAgentSessions reflects it
359
+ * immediately. Pass null/empty to clear.
360
+ */
361
+ renameSession(agentName: string, sessionId: string, name: string | null): Promise<void>;
362
+ /**
363
+ * Trigger an agent with a prompt and stream output via onMessage.
364
+ * Returns the TriggerResult (carries the final sessionId).
365
+ */
366
+ chat(agentName: string, opts: ChatTurnOptions): Promise<TriggerResult>;
367
+ /**
368
+ * Pending async-input-queue depth of a resumed session's `claude` transcript —
369
+ * the residue gate for the resume self-interrupt drain (see `./resume-drain.ts`
370
+ * and {@link drainBacklogThenConsume}). Resolves the agent's CLI transcript from
371
+ * its recorded working directory. Returns 0 (→ no drain, original fast path) for
372
+ * an unknown agent or any read error, so a detection failure never changes turn
373
+ * behavior. Public so the ws.ts wake consumer can gate on it too.
374
+ */
375
+ residueDepthFor(agentName: string, sessionId: string): Promise<number>;
376
+ /**
377
+ * Session-mode counterpart to {@link chat} (Paddock#111). Drives the turn
378
+ * through a persistent, herdctl-managed `openChatSession` (`manageLifecycle:
379
+ * true`) instead of a one-shot `trigger()`, so a `ScheduleWakeup` / `/loop` the
380
+ * turn leaves behind is captured by the reaper (herdctl#307) and re-fired
381
+ * through the scheduler — i.e. cross-turn autonomy actually works. The turn
382
+ * itself streams identically (same `onMessage` shape), so callers are unchanged.
383
+ *
384
+ * Contract differences from {@link chat}:
385
+ * - No herdctl job record exists, so the returned `jobId` is a synthetic turn
386
+ * id we register in {@link liveSessions}; Stop maps to `session.interrupt()`
387
+ * via {@link cancel}.
388
+ * - We do NOT `close()` the session: it's managed, so the reaper tears it down
389
+ * when idle (and captures its wakeups on the Stop hook). Per the core
390
+ * contract, the message stream ending IS the reap.
391
+ */
392
+ chatSession(agentName: string, opts: ChatTurnOptions): Promise<TriggerResult>;
393
+ /**
394
+ * Register the consumer that drives scheduler-fired session wakes (Paddock#111
395
+ * gap 3). herdctl resumes the woken session (`manageLifecycle: true`) and hands
396
+ * it here; the handler must consume the stream onto the hub/transcript so the
397
+ * autonomous turn is visible even though no client is watching. A no-op if the
398
+ * fleet has no lifecycle manager. Safe to call before/after {@link init}.
399
+ */
400
+ onSessionWake(handler: SessionWakeHandler | undefined): void;
401
+ /**
402
+ * Register the resolver herdctl calls to RE-ESTABLISH this session's in-process
403
+ * injected MCP servers when a scheduler-fired wake resumes an idle/reaped session
404
+ * (edspencer/herdctl#390, wired in @herdctl/core 5.22.1). The wake path drives the
405
+ * turn inside herdctl (a `ScheduleWakeup` / `/loop` / `CronCreate` re-fire), so it
406
+ * bypasses Paddock's per-turn injection — without this the resumed `claude`
407
+ * subprocess re-spawns with the `mcp__paddock*__*` tools "allowed" but UNBACKED,
408
+ * so they vanish from the catalog for the whole autonomous stretch (the "MCP flap").
409
+ * The resolver is SYNCHRONOUS (herdctl threads its result into `openChatSession`
410
+ * before the subprocess spawns) and MUST NOT throw — herdctl catches + logs a throw
411
+ * and degrades to no-injection, but we return `undefined` (safe no-injection default)
412
+ * rather than rely on that. A no-op if the fleet has no lifecycle manager; safe to
413
+ * call before/after {@link init}. Thin passthrough, mirroring {@link onSessionWake}.
414
+ */
415
+ setResolveInjectedMcpServers(resolve: ResolveInjectedMcpServers | undefined): void;
416
+ /**
417
+ * Register the consumer that OWNS execution of a fired schedule (issue #265 /
418
+ * DD-1, herdctl#375). When set, herdctl routes every scheduler-fired trigger
419
+ * (interval / cron / forced "trigger now") here instead of running it headless
420
+ * via its built-in ScheduleExecutor — so Paddock runs the turn on its OWN hub /
421
+ * resume path (`startAgentTurn`), keeping the scheduled chat visible + re-attachable
422
+ * (and never `isSidechain`-hidden). A no-op fallback to headless applies if no
423
+ * handler is registered. Thin passthrough, mirroring {@link onSessionWake}.
424
+ */
425
+ onScheduleTrigger(handler: ScheduleTriggerHandler | undefined): void;
426
+ /**
427
+ * The runtime state of a project keeper's schedules (issue #266 / D4) — the
428
+ * live `ScheduleInfo` herdctl tracks (status, `lastRunAt`/`nextRunAt`,
429
+ * `lastError`) for the schedules Paddock forwarded into the keeper agent. The
430
+ * D4 UI merges this with the project.yaml declaration (which carries the
431
+ * Paddock-only `prompt`/`promptFile`/`resume_session`) for the list view. A
432
+ * schedule-less keeper (or one herdctl hasn't finished arming) yields `[]`;
433
+ * errors are swallowed to `[]` so the settings pane degrades to the declared
434
+ * definitions rather than failing to render.
435
+ */
436
+ listAgentSchedules(project: Project): Promise<ScheduleInfo[]>;
437
+ /**
438
+ * Run a slash command (e.g. `/compact`) against an agent's chat session.
439
+ *
440
+ * Unlike {@link chat}, which sends the text as a one-shot prompt via
441
+ * `trigger()`, this drives herdctl's streaming session (`openChatSession`) —
442
+ * the only mode in which the SDK dispatches slash commands and exposes control
443
+ * requests. The command runs against the resumed `sessionId`, so `/compact`
444
+ * compacts the real chat history. SDK messages stream via `onMessage` (same
445
+ * shape as {@link chat}); resolves when the turn completes, then closes the
446
+ * session. Returns the resolved session id.
447
+ */
448
+ runCommand(agentName: string, opts: {
449
+ command: string;
450
+ resume?: string | null;
451
+ onMessage?: (msg: SDKMessage) => void | Promise<void>;
452
+ }): Promise<{
453
+ sessionId: string | null;
454
+ }>;
455
+ /**
456
+ * Cancel a running turn (Stop button → WS chat:cancel). Handles BOTH drive
457
+ * modes off the single id the client holds as `jobId`:
458
+ * - **session mode** — the id is a synthetic turn id in {@link liveSessions};
459
+ * interrupt the live `RuntimeSession` (there is no herdctl job to cancel).
460
+ * - **batch mode** — the id is a real herdctl job id; abort it via `cancelJob`
461
+ * (which kills the CLI subprocess / aborts the SDK query).
462
+ *
463
+ * Returns `true` if something was actually cancelled. Errors are logged rather
464
+ * than silently swallowed (the previous `.catch(() => undefined)` hid every
465
+ * failure, so a broken Stop looked like a no-op).
466
+ */
467
+ cancel(jobId: string): Promise<boolean>;
468
+ /**
469
+ * Run a project's sweeper (curator) agent with a fresh session and the given
470
+ * prompt. Used OUT OF BAND by SweepService — never from the user-chat path —
471
+ * so a sweep can never enqueue another sweep. resume:null forces a clean
472
+ * session each time (the sweep is stateless).
473
+ *
474
+ * The sweeper is tool-less and returns its result as plain assistant text
475
+ * (the two marked sections SweepService parses), so we accumulate the
476
+ * assistant text deltas via @herdctl/chat's shared translator (same pattern
477
+ * as ws.ts) and return them alongside the TriggerResult.
478
+ */
479
+ runSweeper(slug: string, prompt: string): Promise<{
480
+ result: TriggerResult;
481
+ text: string;
482
+ }>;
483
+ /** Recent sessions for a project's keeper agent, used to build the sweep digest. */
484
+ recentSessions(project: Project, limit?: number): Promise<DiscoveredSession[]>;
485
+ /**
486
+ * List a project's chats across ALL of its VISIBLE agents (Epic G / G3, GG-5):
487
+ * the keeper PLUS every declared event-hook agent (`hook-<slug>-<name>`), so a
488
+ * hook's chats show up in the sidebar alongside the keeper's. The sweeper is
489
+ * deliberately excluded — it's the `hideChats` case (its curation chats stay
490
+ * hidden, unchanged). See
491
+ * {@link visibleProjectAgentNames}.
492
+ *
493
+ * Sessions are keyed by working directory in core's discovery, and every one of
494
+ * a project's agents shares the project cwd, so `getAgentSessions` attributes each
495
+ * session to the specific agent that owns it (keeper vs. a hook). We query each
496
+ * visible agent and merge — deduping by session id defensively (attribution is
497
+ * per-agent, so overlap shouldn't happen) — then re-sort mtime-descending to
498
+ * restore the interleaved recency order a single listing would have had. Each
499
+ * per-agent query is fault-isolated: a hook whose agent failed to register (or was
500
+ * just removed) yields `[]` instead of failing the whole list.
501
+ *
502
+ * A **hook-less project** (the overwhelmingly common case) has exactly one visible
503
+ * agent — its keeper — so we short-circuit to the single un-merged listing: same
504
+ * one directory scan (and no dedup/sort work) the pre-G3 keeper-only path did, i.e.
505
+ * zero added cost unless the project actually declares hooks. The extra per-hook
506
+ * scans only kick in for a project that has hook chats to show.
507
+ */
508
+ listSessions(project: Project): Promise<DiscoveredSession[]>;
509
+ /**
510
+ * The on-disk job-record reads (run history + the unread badge) live in
511
+ * `./herdctl-jobs.js` as pure functions over `<stateDir>/jobs` (issue #403);
512
+ * these thin wrappers thread `this.cfg.stateDir` (or, for the two unread-badge
513
+ * reads, this service's {@link jobsIndex}). See there for the full semantics of
514
+ * each (unread signal, per-project grouping, run history).
515
+ */
516
+ lastTurnCompletedAt(): Promise<Map<string, string>>;
517
+ lastTurnCompletedAtByProject(): Promise<Map<string, Map<string, string>>>;
518
+ listProjectRuns(project: Project, limit?: number): Promise<JobMetadata[]>;
519
+ listRunsForAgents(agents: string[], limit?: number): Promise<JobMetadata[]>;
520
+ /**
521
+ * Promote a chat into a NEW project: re-home its transcript into that
522
+ * project's `.chats/` (rewriting the embedded `cwd` so the transcript reports
523
+ * the new project dir) and re-point the session's herdctl job records at the
524
+ * new keeper. After this + the cache invalidations below the chat lists and
525
+ * resumes under the new project with NO restart — doing the same writes from
526
+ * outside the process would need one, to drop the attribution-index cache.
527
+ *
528
+ * Moves one chat from one keeper's store to another's. The root is a workspace
529
+ * like any other, so root chats carry the promote action too.
530
+ *
531
+ * The caller MUST have already created `to` and registered its keeper
532
+ * (ensureProjectAgent), so its `.chats/` + transcript symlink exist. Throws if
533
+ * the source transcript can't be read (e.g. unknown session id).
534
+ */
535
+ promoteSession(sessionId: string, from: Project, to: Project): Promise<void>;
536
+ /**
537
+ * Fork a project chat: eagerly duplicate `sourceSessionId`'s transcript into a
538
+ * brand-new session in the SAME project, so the fork exists immediately — a
539
+ * real, resumable chat with the full parent history visible — rather than being
540
+ * materialized only when the user sends a first message. The source is left
541
+ * untouched (this is a copy, not a move).
542
+ *
543
+ * Same mechanics as {@link promoteSession} (copy the JSONL, keep it
544
+ * discoverable + attributed), with two differences: a NEW session id is minted
545
+ * and rewritten onto every transcript line (so the copy is internally
546
+ * consistent with its new filename — Claude Code stamps appended lines with the
547
+ * file's id, and a mismatch is version-fragile), and the source file is kept.
548
+ * `cwd` is unchanged (the fork stays in the same project). Returns the new id.
549
+ */
550
+ /**
551
+ * Whether a chat transcript exists for `sessionId` in `project`. Used by the
552
+ * self-management MCP write tools (#214) to validate a caller-supplied target
553
+ * before forking / messaging it, so a bad id yields a clean "not found" instead
554
+ * of a raw ENOENT (fork) or a false-positive success (send_message). Rejects a
555
+ * malformed id (path-traversal guard) as non-existent.
556
+ */
557
+ sessionExists(project: Project, sessionId: string): Promise<boolean>;
558
+ forkSession(project: Project, sourceSessionId: string, name?: string, fromUuid?: string): Promise<string>;
559
+ /**
560
+ * Revert a chat back to an earlier message (issue #451): truncate the session's
561
+ * transcript at `toUuid`, in place, keeping the SAME session id — so the chat's
562
+ * identity/URL is preserved and the next turn continues as if the later
563
+ * messages never happened. The dropped tail is backed up (recoverable) to a
564
+ * `.reverts/` sidecar that is deliberately kept out of the discoverable
565
+ * `<id>.jsonl` namespace so it never lists as a chat.
566
+ *
567
+ * NOTE: this rolls back the CONVERSATION only — real-world side-effects of the
568
+ * reverted turns (files written, PRs opened, messages sent) are NOT undone. The
569
+ * caller's UI is responsible for warning about that. Returns the number of
570
+ * transcript records dropped.
571
+ */
572
+ revertSession(project: Project, sessionId: string, toUuid: string): Promise<{
573
+ removed: number;
574
+ }>;
575
+ /**
576
+ * Attribute an *in-flight* session to its agent the moment its id is known —
577
+ * mid first turn — so a brand-new chat lists in the sidebar immediately
578
+ * instead of only after the first turn finishes (issue #100).
579
+ *
580
+ * The core attribution index is built from herdctl job records, and herdctl
581
+ * writes the resolved `session_id` into a run's own job record only on
582
+ * completion. While the first turn runs, that record has `session_id: null`,
583
+ * so the session is unattributed and {@link SessionDiscoveryService.getAgentSessions}
584
+ * filters it out. We close that window with the SAME synthetic-job-record
585
+ * trick used for fork/promote: write an adoption record keyed to the session
586
+ * id, then drop the discovery + attribution caches so the next list call
587
+ * surfaces it. Idempotent — a stable per-session job id means a repeat call
588
+ * just overwrites the same file, and when the real run later finalizes its own
589
+ * record (same session id, same agent) the attribution is unchanged.
590
+ *
591
+ * @param sessionId - The freshly-resolved session id of the running turn
592
+ * @param agentName - The qualified agent the session belongs to
593
+ */
594
+ attributeRunningSession(sessionId: string, agentName: string): Promise<void>;
595
+ /** Read the parsed messages of a session, by agent name. */
596
+ sessionMessages(agentName: string, sessionId: string): Promise<ChatMessage[]>;
597
+ /**
598
+ * Slash commands available to an agent, memoized per agent name. The set is
599
+ * stable per project (built-ins + the project's `.claude/commands` + any
600
+ * MCP-provided commands) and each underlying `listAgentCommands` call spawns a
601
+ * short-lived `claude` streaming subprocess (open → query → close), so we
602
+ * cache aggressively. The cache is process-lifetime: it fills on first use and
603
+ * only clears on server restart — the same boot-only invalidation the fleet's
604
+ * agent registration already relies on. A concurrent first call is
605
+ * deduplicated by caching the in-flight promise, so a burst of composer fetches
606
+ * spawns exactly one subprocess.
607
+ */
608
+ private commandsCache;
609
+ listCommands(agentName: string): Promise<SlashCommand[]>;
610
+ private keeperAgentConfig;
611
+ private sweeperAgentConfig;
612
+ /**
613
+ * Create the sweeper's own working directory (+ symlink its encoded transcript
614
+ * path at a `.chats/` inside it), so the tool-less curator never shares a CLI
615
+ * session directory with the project's keeper (issue #548). Reuses
616
+ * `ensureProjectChats` with the sweeper dir as both cwd and chats host, which
617
+ * also mkdir's the dir itself. Never throws — the helper swallows its own
618
+ * errors, and a missing symlink only means Claude keeps its default location.
619
+ */
620
+ private ensureSweeperHome;
621
+ private triggerAgentConfig;
622
+ private ensureConfigFile;
623
+ }