@edspencer/paddock 0.0.1 → 0.56.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 (294) 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 +512 -0
  33. package/packages/server/dist/config.js +593 -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/event-bus.d.ts +87 -0
  37. package/packages/server/dist/event-bus.js +48 -0
  38. package/packages/server/dist/git.d.ts +128 -0
  39. package/packages/server/dist/git.js +446 -0
  40. package/packages/server/dist/github-auth.d.ts +44 -0
  41. package/packages/server/dist/github-auth.js +169 -0
  42. package/packages/server/dist/herdctl-agent-config.d.ts +98 -0
  43. package/packages/server/dist/herdctl-agent-config.js +331 -0
  44. package/packages/server/dist/herdctl-agent-names.d.ts +172 -0
  45. package/packages/server/dist/herdctl-agent-names.js +228 -0
  46. package/packages/server/dist/herdctl-jobs-index.d.ts +65 -0
  47. package/packages/server/dist/herdctl-jobs-index.js +225 -0
  48. package/packages/server/dist/herdctl-jobs.d.ts +98 -0
  49. package/packages/server/dist/herdctl-jobs.js +225 -0
  50. package/packages/server/dist/herdctl.d.ts +572 -0
  51. package/packages/server/dist/herdctl.js +1218 -0
  52. package/packages/server/dist/hook-config.d.ts +217 -0
  53. package/packages/server/dist/hook-config.js +224 -0
  54. package/packages/server/dist/http-bytes.d.ts +24 -0
  55. package/packages/server/dist/http-bytes.js +52 -0
  56. package/packages/server/dist/index.d.ts +1 -0
  57. package/packages/server/dist/index.js +14 -0
  58. package/packages/server/dist/instance-config.d.ts +135 -0
  59. package/packages/server/dist/instance-config.js +345 -0
  60. package/packages/server/dist/localcommand.d.ts +23 -0
  61. package/packages/server/dist/localcommand.js +140 -0
  62. package/packages/server/dist/management-auth.d.ts +99 -0
  63. package/packages/server/dist/management-auth.js +165 -0
  64. package/packages/server/dist/management-config.d.ts +164 -0
  65. package/packages/server/dist/management-config.js +286 -0
  66. package/packages/server/dist/management-mcp-server.d.ts +20 -0
  67. package/packages/server/dist/management-mcp-server.js +95 -0
  68. package/packages/server/dist/management-metadata.d.ts +67 -0
  69. package/packages/server/dist/management-metadata.js +56 -0
  70. package/packages/server/dist/management-ops.d.ts +110 -0
  71. package/packages/server/dist/management-ops.js +449 -0
  72. package/packages/server/dist/management-policy.d.ts +172 -0
  73. package/packages/server/dist/management-policy.js +259 -0
  74. package/packages/server/dist/message-provenance.d.ts +112 -0
  75. package/packages/server/dist/message-provenance.js +260 -0
  76. package/packages/server/dist/models.d.ts +164 -0
  77. package/packages/server/dist/models.js +208 -0
  78. package/packages/server/dist/openapi.d.ts +66 -0
  79. package/packages/server/dist/openapi.js +183 -0
  80. package/packages/server/dist/parent-detach.d.ts +30 -0
  81. package/packages/server/dist/parent-detach.js +109 -0
  82. package/packages/server/dist/preload.d.ts +43 -0
  83. package/packages/server/dist/preload.js +55 -0
  84. package/packages/server/dist/project-files.d.ts +76 -0
  85. package/packages/server/dist/project-files.js +191 -0
  86. package/packages/server/dist/project-mime.d.ts +35 -0
  87. package/packages/server/dist/project-mime.js +55 -0
  88. package/packages/server/dist/project-paths.d.ts +88 -0
  89. package/packages/server/dist/project-paths.js +126 -0
  90. package/packages/server/dist/project-types.d.ts +316 -0
  91. package/packages/server/dist/project-types.js +48 -0
  92. package/packages/server/dist/projects.d.ts +202 -0
  93. package/packages/server/dist/projects.js +894 -0
  94. package/packages/server/dist/queued-message.d.ts +35 -0
  95. package/packages/server/dist/queued-message.js +116 -0
  96. package/packages/server/dist/read-state.d.ts +46 -0
  97. package/packages/server/dist/read-state.js +139 -0
  98. package/packages/server/dist/recovery-config.d.ts +111 -0
  99. package/packages/server/dist/recovery-config.js +105 -0
  100. package/packages/server/dist/recovery.d.ts +262 -0
  101. package/packages/server/dist/recovery.js +490 -0
  102. package/packages/server/dist/resume-drain.d.ts +122 -0
  103. package/packages/server/dist/resume-drain.js +158 -0
  104. package/packages/server/dist/route-context.d.ts +168 -0
  105. package/packages/server/dist/route-context.js +39 -0
  106. package/packages/server/dist/route-errors.d.ts +8 -0
  107. package/packages/server/dist/route-errors.js +13 -0
  108. package/packages/server/dist/routes/chats.d.ts +12 -0
  109. package/packages/server/dist/routes/chats.js +1406 -0
  110. package/packages/server/dist/routes/git.d.ts +13 -0
  111. package/packages/server/dist/routes/git.js +236 -0
  112. package/packages/server/dist/routes/mcp.d.ts +83 -0
  113. package/packages/server/dist/routes/mcp.js +250 -0
  114. package/packages/server/dist/routes/meta.d.ts +15 -0
  115. package/packages/server/dist/routes/meta.js +447 -0
  116. package/packages/server/dist/routes/projects.d.ts +25 -0
  117. package/packages/server/dist/routes/projects.js +736 -0
  118. package/packages/server/dist/routes/triggers.d.ts +21 -0
  119. package/packages/server/dist/routes/triggers.js +324 -0
  120. package/packages/server/dist/routes/workspace-mount.d.ts +48 -0
  121. package/packages/server/dist/routes/workspace-mount.js +32 -0
  122. package/packages/server/dist/routes.d.ts +26 -0
  123. package/packages/server/dist/routes.js +29 -0
  124. package/packages/server/dist/run-provenance.d.ts +109 -0
  125. package/packages/server/dist/run-provenance.js +206 -0
  126. package/packages/server/dist/runs.d.ts +92 -0
  127. package/packages/server/dist/runs.js +72 -0
  128. package/packages/server/dist/schedule-config.d.ts +68 -0
  129. package/packages/server/dist/schedule-config.js +75 -0
  130. package/packages/server/dist/schedule-session.d.ts +34 -0
  131. package/packages/server/dist/schedule-session.js +120 -0
  132. package/packages/server/dist/self-mcp-descriptions.d.ts +20 -0
  133. package/packages/server/dist/self-mcp-descriptions.js +145 -0
  134. package/packages/server/dist/self-mcp-read.d.ts +15 -0
  135. package/packages/server/dist/self-mcp-read.js +97 -0
  136. package/packages/server/dist/self-mcp-triggers.d.ts +30 -0
  137. package/packages/server/dist/self-mcp-triggers.js +144 -0
  138. package/packages/server/dist/self-mcp-types.d.ts +248 -0
  139. package/packages/server/dist/self-mcp-types.js +10 -0
  140. package/packages/server/dist/self-mcp-util.d.ts +73 -0
  141. package/packages/server/dist/self-mcp-util.js +157 -0
  142. package/packages/server/dist/self-mcp-write.d.ts +35 -0
  143. package/packages/server/dist/self-mcp-write.js +215 -0
  144. package/packages/server/dist/self-mcp.d.ts +95 -0
  145. package/packages/server/dist/self-mcp.js +448 -0
  146. package/packages/server/dist/send-file-mcp.d.ts +46 -0
  147. package/packages/server/dist/send-file-mcp.js +281 -0
  148. package/packages/server/dist/session-hub.d.ts +186 -0
  149. package/packages/server/dist/session-hub.js +291 -0
  150. package/packages/server/dist/spawn-capability.d.ts +99 -0
  151. package/packages/server/dist/spawn-capability.js +106 -0
  152. package/packages/server/dist/star.d.ts +26 -0
  153. package/packages/server/dist/star.js +94 -0
  154. package/packages/server/dist/start.d.ts +1 -0
  155. package/packages/server/dist/start.js +21 -0
  156. package/packages/server/dist/subagents.d.ts +337 -0
  157. package/packages/server/dist/subagents.js +626 -0
  158. package/packages/server/dist/sweep.d.ts +122 -0
  159. package/packages/server/dist/sweep.js +696 -0
  160. package/packages/server/dist/tooldetails.d.ts +33 -0
  161. package/packages/server/dist/tooldetails.js +466 -0
  162. package/packages/server/dist/transcribe.d.ts +69 -0
  163. package/packages/server/dist/transcribe.js +119 -0
  164. package/packages/server/dist/transcripts.d.ts +42 -0
  165. package/packages/server/dist/transcripts.js +164 -0
  166. package/packages/server/dist/trigger-config.d.ts +278 -0
  167. package/packages/server/dist/trigger-config.js +417 -0
  168. package/packages/server/dist/trigger-runtime.d.ts +94 -0
  169. package/packages/server/dist/trigger-runtime.js +82 -0
  170. package/packages/server/dist/trigger-session.d.ts +34 -0
  171. package/packages/server/dist/trigger-session.js +122 -0
  172. package/packages/server/dist/triggers.d.ts +83 -0
  173. package/packages/server/dist/triggers.js +124 -0
  174. package/packages/server/dist/trusted-proxy.d.ts +57 -0
  175. package/packages/server/dist/trusted-proxy.js +157 -0
  176. package/packages/server/dist/turn-notice.d.ts +154 -0
  177. package/packages/server/dist/turn-notice.js +271 -0
  178. package/packages/server/dist/unread.d.ts +36 -0
  179. package/packages/server/dist/unread.js +123 -0
  180. package/packages/server/dist/usage.d.ts +66 -0
  181. package/packages/server/dist/usage.js +243 -0
  182. package/packages/server/dist/wake-injection.d.ts +156 -0
  183. package/packages/server/dist/wake-injection.js +136 -0
  184. package/packages/server/dist/whisper-local.d.ts +9 -0
  185. package/packages/server/dist/whisper-local.js +122 -0
  186. package/packages/server/dist/ws-context.d.ts +158 -0
  187. package/packages/server/dist/ws-context.js +1 -0
  188. package/packages/server/dist/ws-protocol.d.ts +423 -0
  189. package/packages/server/dist/ws-protocol.js +251 -0
  190. package/packages/server/dist/ws-self-mcp.d.ts +52 -0
  191. package/packages/server/dist/ws-self-mcp.js +28 -0
  192. package/packages/server/dist/ws-triggers.d.ts +29 -0
  193. package/packages/server/dist/ws-triggers.js +261 -0
  194. package/packages/server/dist/ws-turn.d.ts +86 -0
  195. package/packages/server/dist/ws-turn.js +751 -0
  196. package/packages/server/dist/ws.d.ts +62 -0
  197. package/packages/server/dist/ws.js +1024 -0
  198. package/packages/server/package.json +6 -0
  199. package/packages/web/dist/assets/InstanceConfigPage-4cQGTj9Y.js +2 -0
  200. package/packages/web/dist/assets/MarkdownRenderer-CJZFLj_8.js +30 -0
  201. package/packages/web/dist/assets/ProjectRedirect-BHwVbbqB.js +2 -0
  202. package/packages/web/dist/assets/ProjectView-BOvuAITg.js +13 -0
  203. package/packages/web/dist/assets/ProjectsGrid-BCojZOGY.js +2 -0
  204. package/packages/web/dist/assets/arc-BVyxvnkl.js +2 -0
  205. package/packages/web/dist/assets/architectureDiagram-3BPJPVTR-Cd9Puqg1.js +37 -0
  206. package/packages/web/dist/assets/bash-I8pq0VWm.js +2 -0
  207. package/packages/web/dist/assets/blockDiagram-GPEHLZMM-B_AQt_Rx.js +133 -0
  208. package/packages/web/dist/assets/c-D2F1sQUL.js +2 -0
  209. package/packages/web/dist/assets/c4Diagram-AAUBKEIU-7e0TQtXB.js +11 -0
  210. package/packages/web/dist/assets/channel-C_1aISNn.js +2 -0
  211. package/packages/web/dist/assets/chunk-2J33WTMH-Dz4WZmUZ.js +2 -0
  212. package/packages/web/dist/assets/chunk-4BX2VUAB-DVWYldo6.js +2 -0
  213. package/packages/web/dist/assets/chunk-55IACEB6-BIMrizQd.js +2 -0
  214. package/packages/web/dist/assets/chunk-727SXJPM-CL2q9fF1.js +207 -0
  215. package/packages/web/dist/assets/chunk-AQP2D5EJ-BSq3HlQ9.js +232 -0
  216. package/packages/web/dist/assets/chunk-FMBD7UC4-Bgt64pLE.js +16 -0
  217. package/packages/web/dist/assets/chunk-ND2GUHAM-C8Pr2A38.js +2 -0
  218. package/packages/web/dist/assets/chunk-QZHKN3VN-BAOqh-MN.js +2 -0
  219. package/packages/web/dist/assets/classDiagram-4FO5ZUOK-B8JE7cHJ.js +2 -0
  220. package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-B8JE7cHJ.js +2 -0
  221. package/packages/web/dist/assets/core-lJeGACQg.js +4 -0
  222. package/packages/web/dist/assets/cose-bilkent-S5V4N54A-BF4tameA.js +2 -0
  223. package/packages/web/dist/assets/cpp-Bm3ii6kY.js +2 -0
  224. package/packages/web/dist/assets/csharp-BNpuYZMG.js +2 -0
  225. package/packages/web/dist/assets/css-DazXZka4.js +2 -0
  226. package/packages/web/dist/assets/cytoscape.esm-CUqq0XTU.js +332 -0
  227. package/packages/web/dist/assets/dagre-BM42HDAG-DdsLM6ql.js +5 -0
  228. package/packages/web/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  229. package/packages/web/dist/assets/diagram-2AECGRRQ-BQ78S_RM.js +44 -0
  230. package/packages/web/dist/assets/diagram-5GNKFQAL-CAKE6nHE.js +11 -0
  231. package/packages/web/dist/assets/diagram-KO2AKTUF-fEvHRXgr.js +4 -0
  232. package/packages/web/dist/assets/diagram-LMA3HP47-CtWsgeSm.js +25 -0
  233. package/packages/web/dist/assets/diagram-OG6HWLK6-Buog8k7_.js +25 -0
  234. package/packages/web/dist/assets/erDiagram-TEJ5UH35-CWvVVPZS.js +86 -0
  235. package/packages/web/dist/assets/flowDiagram-I6XJVG4X-DsCGXtp4.js +163 -0
  236. package/packages/web/dist/assets/format-DamJQ9LO.js +2 -0
  237. package/packages/web/dist/assets/ganttDiagram-6RSMTGT7-CsdwIvvC.js +293 -0
  238. package/packages/web/dist/assets/gitGraphDiagram-PVQCEYII-hZvrX1sD.js +107 -0
  239. package/packages/web/dist/assets/go-BPx_y7j8.js +2 -0
  240. package/packages/web/dist/assets/graph-BlwPajkw.js +2 -0
  241. package/packages/web/dist/assets/index-CGbZdrm8.js +71 -0
  242. package/packages/web/dist/assets/index-DJDQ210-.css +1 -0
  243. package/packages/web/dist/assets/infoDiagram-5YYISTIA-nE_Vtz_l.js +3 -0
  244. package/packages/web/dist/assets/ini-B3BVgEsG.js +2 -0
  245. package/packages/web/dist/assets/init-Gi6I4Gst.js +2 -0
  246. package/packages/web/dist/assets/ishikawaDiagram-YF4QCWOH-2Nlqs0oH.js +71 -0
  247. package/packages/web/dist/assets/java-B5VKqtH2.js +2 -0
  248. package/packages/web/dist/assets/javascript-BKRaQes9.js +2 -0
  249. package/packages/web/dist/assets/journeyDiagram-JHISSGLW-mjU1fMc4.js +140 -0
  250. package/packages/web/dist/assets/json-DIYVocXf.js +2 -0
  251. package/packages/web/dist/assets/kanban-definition-UN3LZRKU-C_9xNaa3.js +90 -0
  252. package/packages/web/dist/assets/katex-C5jXJg4s.js +258 -0
  253. package/packages/web/dist/assets/kotlin-Cw-eF_bm.js +3 -0
  254. package/packages/web/dist/assets/lastTab-CU1zsF0j.js +2 -0
  255. package/packages/web/dist/assets/layout-Boy8vhSP.js +2 -0
  256. package/packages/web/dist/assets/linear-BxKd91ho.js +2 -0
  257. package/packages/web/dist/assets/mermaid.core-C-CuhD8I.js +310 -0
  258. package/packages/web/dist/assets/mindmap-definition-RKZ34NQL-Bs6yfcZv.js +97 -0
  259. package/packages/web/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  260. package/packages/web/dist/assets/php-CRdd4dfS.js +3 -0
  261. package/packages/web/dist/assets/pieDiagram-4H26LBE5-DNdqX7O5.js +31 -0
  262. package/packages/web/dist/assets/python-kSlZHhJb.js +2 -0
  263. package/packages/web/dist/assets/quadrantDiagram-W4KKPZXB-ZMJl9OdZ.js +8 -0
  264. package/packages/web/dist/assets/queued-LamEU0GH.js +2 -0
  265. package/packages/web/dist/assets/requirementDiagram-4Y6WPE33-RmxMdm2q.js +85 -0
  266. package/packages/web/dist/assets/ruby-7T4ktxyv.js +2 -0
  267. package/packages/web/dist/assets/rust-HC3MBUhY.js +2 -0
  268. package/packages/web/dist/assets/sankeyDiagram-5OEKKPKP-OAAhWcfK.js +41 -0
  269. package/packages/web/dist/assets/scss-DZLA7YXz.js +2 -0
  270. package/packages/web/dist/assets/sequenceDiagram-3UESZ5HK-BWF7rCyN.js +163 -0
  271. package/packages/web/dist/assets/sql-BC88IN-V.js +2 -0
  272. package/packages/web/dist/assets/stateDiagram-AJRCARHV-M3b_zqqo.js +2 -0
  273. package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-CI7saFDa.js +2 -0
  274. package/packages/web/dist/assets/swift-Dn8kKO_1.js +2 -0
  275. package/packages/web/dist/assets/timeline-definition-PNZ67QCA-BpOCh63N.js +121 -0
  276. package/packages/web/dist/assets/typescript-C2FFdlUC.js +2 -0
  277. package/packages/web/dist/assets/vennDiagram-CIIHVFJN-D_iPH45Q.js +35 -0
  278. package/packages/web/dist/assets/wardley-L42UT6IY-DVD9I2Om.js +174 -0
  279. package/packages/web/dist/assets/wardleyDiagram-YWT4CUSO-Bn1IKwjn.js +79 -0
  280. package/packages/web/dist/assets/xml-BXBhIUeX.js +2 -0
  281. package/packages/web/dist/assets/xychartDiagram-2RQKCTM6-DwtI0bkR.js +8 -0
  282. package/packages/web/dist/assets/yaml-drsDnIKT.js +2 -0
  283. package/packages/web/dist/favicon.ico +0 -0
  284. package/packages/web/dist/fonts/inter-latin.woff2 +0 -0
  285. package/packages/web/dist/fonts/jetbrains-mono-latin.woff2 +0 -0
  286. package/packages/web/dist/icons/apple-touch-icon.png +0 -0
  287. package/packages/web/dist/icons/favicon-16.png +0 -0
  288. package/packages/web/dist/icons/favicon-32.png +0 -0
  289. package/packages/web/dist/icons/icon-192.png +0 -0
  290. package/packages/web/dist/icons/icon-512.png +0 -0
  291. package/packages/web/dist/icons/maskable-512.png +0 -0
  292. package/packages/web/dist/index.html +56 -0
  293. package/packages/web/dist/manifest.webmanifest +32 -0
  294. package/packages/web/dist/sw.js +139 -0
@@ -0,0 +1,215 @@
1
+ import { ok, fail, errText, truncateText, redactPaths, resolveModelArg, coercePrompts, FORK_BATCH_MAX, } from "./self-mcp-util.js";
2
+ // The SAME shape validators `ProjectStore.create` uses, so the tool's up-front
3
+ // checks can't drift from the store's (issue #467). `project-paths.ts` is pure
4
+ // (node:path only), so importing it keeps these handlers fleet-free/unit-testable.
5
+ import { SLUG_RE, isValidRepoUrl } from "./project-paths.js";
6
+ import { PROJECT_STATUSES } from "./project-types.js";
7
+ export function createChatHandler(write) {
8
+ return async (args) => {
9
+ try {
10
+ const prompt = typeof args.prompt === "string" ? args.prompt.trim() : "";
11
+ if (!prompt)
12
+ return fail("Error: `prompt` is required (the first turn for the new chat).");
13
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
14
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
15
+ const name = typeof args.name === "string" && args.name.trim().length > 0 ? args.name.trim() : undefined;
16
+ const preloadContext = typeof args.preload_context === "boolean" ? args.preload_context : undefined;
17
+ // Per-chat model override (#336): validated against the picker allow-list; a
18
+ // non-blank unknown id is rejected with an actionable error instead of being
19
+ // silently ignored.
20
+ const modelResult = resolveModelArg(args.model);
21
+ if (typeof modelResult === "string")
22
+ return fail(modelResult);
23
+ const model = modelResult.model;
24
+ const { sessionId } = await write.createChat(project, prompt, { name, preloadContext, model });
25
+ // Echo the human-readable name + kickoff prompt so the chat renders with its
26
+ // real title (not just a link) both live and on reload (#253). When no name
27
+ // was given the web derives a title from the prompt (matching the sidebar's
28
+ // auto-name). Prompt is capped to bound the tool-result payload. `model` is
29
+ // echoed (when overridden) so the tool result records which model the spawned
30
+ // chat runs on (#336).
31
+ return ok({ created: true, project, sessionId, name, model, prompt: truncateText(prompt) });
32
+ }
33
+ catch (error) {
34
+ return fail(`Error creating chat: ${errText(error)}`);
35
+ }
36
+ };
37
+ }
38
+ export function forkChatHandler(write) {
39
+ return async (args) => {
40
+ try {
41
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
42
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
43
+ const explicit = typeof args.session_id === "string" ? args.session_id.trim() : "";
44
+ const sourceSessionId = explicit.length > 0 ? explicit : write.currentSessionId();
45
+ if (!sourceSessionId) {
46
+ return fail("no chat to fork (current chat id not yet known — pass session_id)");
47
+ }
48
+ const prompt = typeof args.prompt === "string" && args.prompt.trim().length > 0 ? args.prompt.trim() : undefined;
49
+ const name = typeof args.name === "string" && args.name.trim().length > 0 ? args.name.trim() : undefined;
50
+ // Per-chat model override (#336): validated against the picker allow-list.
51
+ // Only applies to a kickoff turn — a fork without `prompt` runs no turn.
52
+ const modelResult = resolveModelArg(args.model);
53
+ if (typeof modelResult === "string")
54
+ return fail(modelResult);
55
+ const model = modelResult.model;
56
+ const { sessionId } = await write.forkChat({ projectSlug: project, sourceSessionId, prompt, name, model });
57
+ return ok({
58
+ forked: true,
59
+ project,
60
+ sessionId,
61
+ from: sourceSessionId,
62
+ name,
63
+ model,
64
+ prompt: prompt ? truncateText(prompt) : undefined,
65
+ });
66
+ }
67
+ catch (error) {
68
+ return fail(`Error forking chat: ${errText(error)}`);
69
+ }
70
+ };
71
+ }
72
+ export function sendMessageHandler(write) {
73
+ return async (args) => {
74
+ try {
75
+ const sessionId = typeof args.session_id === "string" ? args.session_id.trim() : "";
76
+ const prompt = typeof args.prompt === "string" ? args.prompt.trim() : "";
77
+ if (!sessionId)
78
+ return fail("Error: `session_id` is required (get it from list_chats).");
79
+ if (!prompt)
80
+ return fail("Error: `prompt` is required (the message to send).");
81
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
82
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
83
+ await write.sendMessage(project, sessionId, prompt);
84
+ // Echo the sent message so the tool renders the actual message text (#253).
85
+ return ok({ sent: true, project, sessionId, prompt: truncateText(prompt) });
86
+ }
87
+ catch (error) {
88
+ return fail(`Error sending message: ${errText(error)}`);
89
+ }
90
+ };
91
+ }
92
+ /**
93
+ * Handler factory for archive_chat / unarchive_chat. Both resolve the target the
94
+ * same way — `session_id` OPTIONAL, defaulting to the CURRENT chat so an agent can
95
+ * archive ITSELF without knowing its own id (mirrors fork_chat's self-default) —
96
+ * and differ only in the boolean they set. `archived` selects which tool this is.
97
+ */
98
+ export function archiveChatHandler(write, archived) {
99
+ const verb = archived ? "archiving" : "unarchiving";
100
+ return async (args) => {
101
+ try {
102
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
103
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
104
+ const explicit = typeof args.session_id === "string" ? args.session_id.trim() : "";
105
+ const sessionId = explicit.length > 0 ? explicit : write.currentSessionId();
106
+ if (!sessionId) {
107
+ return fail("no chat to archive (current chat id not yet known — pass session_id)");
108
+ }
109
+ await write.setArchived(project, sessionId, archived);
110
+ return ok({ archived, project, sessionId });
111
+ }
112
+ catch (error) {
113
+ return fail(`Error ${verb} chat: ${errText(error)}`);
114
+ }
115
+ };
116
+ }
117
+ /**
118
+ * `create_project` (issue #467) — provision a whole new project. The one write tool
119
+ * that does NOT act inside an existing project, which is why it sits behind its own
120
+ * `projectsMcpEnabled` gate.
121
+ *
122
+ * Pure, like its siblings: it trims + SHAPE-validates the args (using the very same
123
+ * `SLUG_RE` / `isValidRepoUrl` / `PROJECT_STATUSES` the store and DTO use, so there
124
+ * is no second definition of "valid") and delegates to `write.createProject`, which
125
+ * runs `ProjectStore.create` + `ensureProjectAgent` — literally the REST create path.
126
+ * The up-front checks are a fast, actionable-error convenience, NOT a replacement for
127
+ * the store's own validation: the store still rejects a duplicate slug, a bad URL and
128
+ * an unclonable repo, and still rolls the whole project dir back on a clone failure.
129
+ */
130
+ export function createProjectHandler(write) {
131
+ return async (args) => {
132
+ try {
133
+ const name = typeof args.name === "string" ? args.name.trim() : "";
134
+ if (!name)
135
+ return fail("Error: `name` is required (the project's display name).");
136
+ const slugArg = typeof args.slug === "string" ? args.slug.trim() : "";
137
+ // Only validate a slug the caller actually SUPPLIED — an omitted slug is
138
+ // derived from `name` by the store (slugify), which can't produce a bad one.
139
+ if (slugArg && !SLUG_RE.test(slugArg)) {
140
+ return fail(`Error: invalid slug "${slugArg}" — must be kebab-case (lowercase a-z, 0-9 and ` +
141
+ "single hyphens, e.g. \"my-new-project\"). Omit `slug` to derive it from `name`.");
142
+ }
143
+ const repoArg = typeof args.repo === "string" ? args.repo.trim() : "";
144
+ if (repoArg && !isValidRepoUrl(repoArg)) {
145
+ return fail(`Error: invalid repo URL "${repoArg}" — expected an https://, git://, ssh:// or ` +
146
+ "git@host:owner/repo url. Omit `repo` to create a notebook project.");
147
+ }
148
+ const statusArg = typeof args.status === "string" ? args.status.trim().toLowerCase() : "";
149
+ if (statusArg && !PROJECT_STATUSES.includes(statusArg)) {
150
+ return fail(`Error: unknown status "${statusArg}". Valid statuses: ${PROJECT_STATUSES.join(", ")}.`);
151
+ }
152
+ const summary = typeof args.summary === "string" ? args.summary.trim() : "";
153
+ const area = typeof args.area === "string" ? args.area.trim() : "";
154
+ const input = {
155
+ name,
156
+ ...(slugArg ? { slug: slugArg } : {}),
157
+ ...(repoArg ? { repo: repoArg } : {}),
158
+ ...(summary ? { summary } : {}),
159
+ ...(area ? { group: area } : {}),
160
+ ...(statusArg ? { status: statusArg } : {}),
161
+ };
162
+ const project = await write.createProject(input);
163
+ return ok({ created: true, ...project });
164
+ }
165
+ catch (error) {
166
+ // Redact server filesystem paths: a clone failure arrives as git's whole argv
167
+ // (`Command failed: git clone -- <url> <dest>`), and the agent needs the repo
168
+ // URL + git's reason, not our on-disk layout. Capped so a verbose git failure
169
+ // can't flood the tool result.
170
+ return fail(`Error creating project: ${truncateText(redactPaths(errText(error)))}`);
171
+ }
172
+ };
173
+ }
174
+ export function forkChatBatchHandler(write) {
175
+ return async (args) => {
176
+ try {
177
+ const prompts = coercePrompts(args.prompts);
178
+ if (prompts.length === 0) {
179
+ return fail("Error: `prompts` is required — a non-empty list of directive strings " +
180
+ "(a JSON array, or one directive per line).");
181
+ }
182
+ if (prompts.length > FORK_BATCH_MAX) {
183
+ return fail(`Error: too many prompts (${prompts.length}); max ${FORK_BATCH_MAX} forks per call.`);
184
+ }
185
+ if (prompts.some((p) => p.length === 0)) {
186
+ return fail("Error: every entry in `prompts` must be a non-empty string.");
187
+ }
188
+ const clean = prompts;
189
+ const projectArg = typeof args.project === "string" ? args.project.trim() : "";
190
+ const project = projectArg.length > 0 ? projectArg : write.currentProjectSlug;
191
+ const explicit = typeof args.session_id === "string" ? args.session_id.trim() : "";
192
+ const sourceSessionId = explicit.length > 0 ? explicit : write.currentSessionId();
193
+ if (!sourceSessionId) {
194
+ return fail("no chat to fork (current chat id not yet known — pass session_id)");
195
+ }
196
+ const namePrefix = typeof args.name_prefix === "string" && args.name_prefix.trim().length > 0 ? args.name_prefix.trim() : undefined;
197
+ // Per-chat model override (#336): one model for the WHOLE fan-out (each fork's
198
+ // kickoff turn runs on it), validated against the picker allow-list.
199
+ const modelResult = resolveModelArg(args.model);
200
+ if (typeof modelResult === "string")
201
+ return fail(modelResult);
202
+ const model = modelResult.model;
203
+ // Concurrent fan-out; herdctl enforces the real concurrency cap downstream.
204
+ const forks = await Promise.all(clean.map(async (prompt, i) => {
205
+ const name = namePrefix ? `${namePrefix} ${i + 1}` : undefined;
206
+ const { sessionId } = await write.forkChat({ projectSlug: project, sourceSessionId, prompt, name, model });
207
+ return { sessionId, prompt };
208
+ }));
209
+ return ok({ count: forks.length, source: sourceSessionId, model, forks });
210
+ }
211
+ catch (error) {
212
+ return fail(`Error forking chat batch: ${errText(error)}`);
213
+ }
214
+ };
215
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Paddock self-management MCP — assembly root (issue #214).
3
+ *
4
+ * Exposes the `paddock_manage` tools that let a keeper agent inspect and drive
5
+ * Paddock itself: enumerate projects/chats + read transcripts (READ, always on),
6
+ * create/fork/message/archive chats (WRITE, Phase 2), and manage a project's
7
+ * unified triggers (Epic T / T3). This module is the thin assembly layer — it
8
+ * wires the extracted pieces into one {@link InjectedMcpServerDef}, conditionally
9
+ * by capability tier, and owns the public name-map exports. The actual logic lives
10
+ * in siblings, all re-exported here so existing importers keep one import path:
11
+ * - `self-mcp-types.ts` — the DTOs + the two per-turn context bags
12
+ * - `self-mcp-util.ts` — result envelopes, clamps, arg coercion + caps
13
+ * - `self-mcp-descriptions.ts` — the agent-facing `*_DESC` prose
14
+ * - `self-mcp-read.ts` — list_projects / list_chats / read_chat handlers
15
+ * - `self-mcp-write.ts` — create/fork/send/archive/fork-batch + create_project handlers
16
+ * - `self-mcp-triggers.ts` — list/set/remove/run trigger handlers
17
+ *
18
+ * ── How it reaches the keeper ───────────────────────────────────────────────
19
+ * Injected via herdctl's `injectedMcpServers` mechanism, exactly like the
20
+ * send_file tool (see send-file-mcp.ts, which documents both routes): on the SDK
21
+ * runtime — how chats run by default — the def becomes an in-process SDK MCP
22
+ * server; on the CLI runtime (the sweeper, triggers, `driveMode: batch`) the
23
+ * agent is a separate `claude -p` process that can't reach one, so herdctl stands
24
+ * up a localhost HTTP MCP bridge instead. Either way herdctl auto-allowlists the
25
+ * server's `mcp__<name>__*` tools. The tools
26
+ * surface to the agent as `mcp__paddock_manage__{list_projects,list_chats,…}`.
27
+ *
28
+ * The module stays pure: it takes narrow bags of async callbacks (wired to
29
+ * ProjectStore/HerdctlService by the caller) so the trimming/validation logic is
30
+ * unit-testable without the fleet. Tool outputs are plain JSON text for the AGENT
31
+ * to read (not a render envelope like send_file).
32
+ */
33
+ import type { InjectedMcpServerDef } from "@herdctl/core";
34
+ import type { SelfMcpContext, SelfMcpWriteContext } from "./self-mcp-types.js";
35
+ export type { SelfMcpProject, SelfMcpChat, SelfMcpMessage, SelfMcpTrigger, SelfMcpContext, SelfMcpWriteContext, SelfMcpCreateProjectInput, SelfMcpCreatedProject, } from "./self-mcp-types.js";
36
+ export { FORK_BATCH_MAX, redactPaths, READ_CHAT_DEFAULT_LIMIT, READ_CHAT_MAX_LIMIT, READ_CHAT_MAX_TEXT, clampLimit, truncateText, resolveModelArg, coercePrompts, coerceToolList, coerceBoolean, } from "./self-mcp-util.js";
37
+ /** Assembly-time options for {@link selfMcpServerDef}. */
38
+ export interface SelfMcpServerDefOptions {
39
+ /**
40
+ * Which tools to OFFER, by bare tool name (`"read_chat"`, not the
41
+ * `mcp__paddock_manage__` form). Used by the Management API (#312) to hide
42
+ * out-of-scope verbs from an external principal. Omit to offer everything.
43
+ */
44
+ toolFilter?: (toolName: string) => boolean;
45
+ }
46
+ /**
47
+ * Build the injected MCP server definition for the self-management tools, bound to
48
+ * a per-turn context. The READ tools (list_projects/list_chats/read_chat) are
49
+ * ALWAYS included. When a {@link SelfMcpWriteContext} is provided (the stricter
50
+ * write flag is on), the WRITE tools (create_chat/fork_chat/send_message/
51
+ * archive_chat/unarchive_chat/fork_chat_batch) are appended too; omit it for
52
+ * unchanged read-only behavior. Two further blocks hang off independent gates ON
53
+ * that write context: {@link SelfMcpWriteContext.projectsMcpEnabled} (issue #467)
54
+ * appends the project tool (create_project), and
55
+ * {@link SelfMcpWriteContext.triggersMcpEnabled} (the per-project trigger-MCP
56
+ * opt-in, Epic T / T3) appends the unified trigger-management tools (list_triggers/
57
+ * set_trigger/remove_trigger/run_trigger). Inject under
58
+ * {@link SELF_MCP_SERVER_KEY}.
59
+ */
60
+ export declare function selfMcpServerDef(context: SelfMcpContext, write?: SelfMcpWriteContext, opts?: SelfMcpServerDefOptions): InjectedMcpServerDef;
61
+ /** The record key + the fully-qualified tool names the agent sees. */
62
+ export declare const SELF_MCP_SERVER_KEY = "paddock_manage";
63
+ export declare const SELF_MCP_TOOL_NAMES: {
64
+ readonly listProjects: "mcp__paddock_manage__list_projects";
65
+ readonly listChats: "mcp__paddock_manage__list_chats";
66
+ readonly readChat: "mcp__paddock_manage__read_chat";
67
+ };
68
+ /** The fully-qualified names of the Phase-2 WRITE tools (only present when the write flag is on). */
69
+ export declare const SELF_MCP_WRITE_TOOL_NAMES: {
70
+ readonly createChat: "mcp__paddock_manage__create_chat";
71
+ readonly forkChat: "mcp__paddock_manage__fork_chat";
72
+ readonly sendMessage: "mcp__paddock_manage__send_message";
73
+ readonly archiveChat: "mcp__paddock_manage__archive_chat";
74
+ readonly unarchiveChat: "mcp__paddock_manage__unarchive_chat";
75
+ readonly forkChatBatch: "mcp__paddock_manage__fork_chat_batch";
76
+ };
77
+ /**
78
+ * The fully-qualified name of the PROJECT tool (issue #467), present only when a
79
+ * write context has {@link SelfMcpWriteContext.projectsMcpEnabled} on.
80
+ */
81
+ export declare const SELF_MCP_PROJECT_TOOL_NAMES: {
82
+ readonly createProject: "mcp__paddock_manage__create_project";
83
+ };
84
+ /**
85
+ * The fully-qualified names of the Epic T / T3 unified trigger-management tools
86
+ * (only present when a write context has {@link SelfMcpWriteContext.triggersMcpEnabled}
87
+ * on). These collapse the former schedule (set/remove/list_schedule) + hook
88
+ * (set/remove/list_hook) verbs.
89
+ */
90
+ export declare const SELF_MCP_TRIGGER_TOOL_NAMES: {
91
+ readonly listTriggers: "mcp__paddock_manage__list_triggers";
92
+ readonly setTrigger: "mcp__paddock_manage__set_trigger";
93
+ readonly removeTrigger: "mcp__paddock_manage__remove_trigger";
94
+ readonly runTrigger: "mcp__paddock_manage__run_trigger";
95
+ };