@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,210 @@
1
+ /**
2
+ * Chat DTO + chat-list helpers for the REST surface. Pure, module-level, and
3
+ * shared by the chat/project route groups: the wire `ChatUsage` shape and its
4
+ * `toChatUsage` builder, the `toChatDto` projection, the preview-cleaning
5
+ * `buildProjectChats`, the `makeTriggerResolver` capability-descriptor resolver,
6
+ * and the run-history page-size constants + `clampRunsLimit`.
7
+ */
8
+ import type { DiscoveredSession } from "@herdctl/core";
9
+ import type { Project } from "./projects.js";
10
+ import { type SessionTokenUsage } from "./usage.js";
11
+ import type { RunProvenance } from "./run-provenance.js";
12
+ import { type ChatTriggerInfo } from "./trigger-config.js";
13
+ /** A session id safe to interpolate into a transcript file path (issue #329). */
14
+ export declare const SAFE_SESSION_ID: RegExp;
15
+ /**
16
+ * The chat that created this one, as sent on the wire — what the sidebar files a
17
+ * child under. `project` is carried because a parent can live in ANOTHER project
18
+ * (a keeper spawning into a sibling repo); the list renders those as roots rather
19
+ * than hiding a chat under a parent the user can't see from here.
20
+ */
21
+ export interface ChatParentRef {
22
+ project: string;
23
+ sessionId: string;
24
+ /** The parent's display name at creation time — a fallback label only. */
25
+ name?: string;
26
+ }
27
+ /**
28
+ * Does this recorded provenance describe a chat that is a root by construction?
29
+ *
30
+ * `depth` counts spawn hops from the human/scheduled root, so depth 0 IS the root
31
+ * — and a root-origin marker says the same thing from the other direction. Either
32
+ * one means "this chat has no parent", as opposed to "this chat's parent was
33
+ * never recorded", which is the only case the inference tier exists to cover.
34
+ */
35
+ export declare function isRecordedRoot(p: RunProvenance): boolean;
36
+ /**
37
+ * Build the `parentOf` resolver for {@link buildProjectChats} — the chat-list
38
+ * parent edge, resolved in two tiers behind one override:
39
+ *
40
+ * 0. `ParentDetachStore` — the user explicitly DETACHED this chat (#508). Checked
41
+ * ahead of both tiers, because detach cannot be expressed by clearing an edge:
42
+ * most live edges are INFERRED (tier 2), so a cleared edge is simply re-derived
43
+ * on the next load. Detached ⇒ root, full stop.
44
+ * 1. `RunProvenance.parentSessionId` — the RECORDED edge, stamped at creation.
45
+ * 2. `MessageProvenanceStore.parentChat()` — a backfill for chats created before
46
+ * tier 1 existed, inferred from who injected the kickoff prompt.
47
+ *
48
+ * Tier 2 only applies to a chat whose parent is genuinely UNKNOWN. A chat with a
49
+ * recorded root marker (#491) is skipped outright: it isn't missing an edge, it
50
+ * has none. Without that guard, the documented report-back workflow re-parents a
51
+ * human's own chat — manager spawns child, child `send_message`s its report home,
52
+ * the manager now carries a `chat`-kind marker and infers its own child as its
53
+ * parent. Both edges then point at each other and `buildChatTree`'s cycle guard
54
+ * decides, per render, which of the two gets promoted to a root.
55
+ *
56
+ * All reads are in-memory sidecar lookups, so this is cheap enough to resolve
57
+ * inline for every row (unlike the usage ring, #116). A chat with neither tier is
58
+ * simply a root.
59
+ */
60
+ export declare function makeParentResolver(runProvenance: {
61
+ get(sessionId: string): Promise<RunProvenance | undefined>;
62
+ }, messageProvenance: {
63
+ parentChat(sessionId: string): Promise<{
64
+ project: string;
65
+ sessionId: string;
66
+ name?: string;
67
+ } | null>;
68
+ }, projectSlug: string,
69
+ /**
70
+ * Has the user detached this chat from its parent (#508)? Optional so the many
71
+ * call sites that predate detach — and the unit tests — stay correct without it;
72
+ * omitted means "nothing is detached".
73
+ */
74
+ isDetached?: (sessionId: string) => Promise<boolean>): (s: DiscoveredSession) => Promise<ChatParentRef | null>;
75
+ /**
76
+ * Cap on how many chats one batch subtree operation may touch (#508). The client
77
+ * derives the set from a rendered subtree, so it is bounded by the chat list in
78
+ * practice; this is the "a hand-rolled request can't ask us to rewrite the whole
79
+ * sidecar" bound, not a UX limit.
80
+ */
81
+ export declare const BATCH_SESSIONS_MAX = 500;
82
+ /**
83
+ * Validate + normalise a batch route's `sessionIds` body field (#508).
84
+ *
85
+ * Returns the de-duplicated ids, or `null` when the body is unusable — which the
86
+ * routes turn into a 400. Deliberately ALL-OR-NOTHING: one malformed id fails the
87
+ * whole request rather than silently applying to the rest, because these
88
+ * operations are meant to keep a chat family in one state. Silently dropping an id
89
+ * would produce exactly the torn family the batch routes exist to prevent.
90
+ */
91
+ export declare function normalizeBatchSessionIds(raw: unknown): string[] | null;
92
+ /** Claude Code's own preview cap (mirrors extractFirstMessagePreview). */
93
+ export declare const PREVIEW_MAX = 100;
94
+ /**
95
+ * Reserved read-state session id for the per-project, per-user "runs last seen"
96
+ * watermark (the since-last-visit digest, #268). A real Claude Code session id is
97
+ * a UUID (`/^[0-9a-f-]+$/`), so the double-underscore sentinel can never collide
98
+ * with one — the watermark keys cleanly alongside per-chat read-state.
99
+ */
100
+ export declare const RUNS_SEEN_SESSION = "__runs__";
101
+ /** Default + cap for the run-history page size. */
102
+ export declare const RUNS_LIMIT_DEFAULT = 100;
103
+ export declare const RUNS_LIMIT_MAX = 500;
104
+ /** Parse + clamp the `?limit=` query for the run-history endpoint. */
105
+ export declare function clampRunsLimit(raw: string | undefined): number;
106
+ /**
107
+ * A chat's usage for the UI: the last-turn context fill (issue #77) plus the
108
+ * chat's cumulative lifetime token totals and a ballpark dollar estimate at API
109
+ * rates (issue #152). The cumulative totals and `costUsd` include every sub-agent
110
+ * the chat spawned (issue #242); `contextTokens` stays main-only (last-turn
111
+ * window fill). `costUsd` is null for a model with no known pricing.
112
+ */
113
+ export type ChatUsage = {
114
+ contextTokens: number;
115
+ contextLimit: number;
116
+ inputTokens: number;
117
+ outputTokens: number;
118
+ cacheReadTokens: number;
119
+ cacheCreationTokens: number;
120
+ totalTokens: number;
121
+ costUsd: number | null;
122
+ };
123
+ /**
124
+ * Which chats the bulk usage endpoint computes rings for (issue #537). Usage has
125
+ * no stored counter — it is derived by streaming each transcript end to end — so
126
+ * scoping the request to what the sidebar actually renders is the difference
127
+ * between a few MB and a few hundred. `active` is the default because the
128
+ * Archived group is collapsed on open.
129
+ */
130
+ export declare const CHAT_USAGE_SCOPES: readonly ["active", "archived", "all"];
131
+ /** One of {@link CHAT_USAGE_SCOPES}. */
132
+ export type ChatUsageScope = (typeof CHAT_USAGE_SCOPES)[number];
133
+ /**
134
+ * Build the wire `ChatUsage` from a parsed {@link SessionTokenUsage} and the
135
+ * chat's model. Returns null when the transcript has no usage yet, so the ring
136
+ * simply hides. `totalTokens` is a headline "tokens this chat consumed" figure —
137
+ * output plus the (context-growing) input/cache reads — while `costUsd` prices
138
+ * each class separately (see {@link estimateCostUsd}).
139
+ */
140
+ export declare function toChatUsage(u: SessionTokenUsage, model: string): ChatUsage | null;
141
+ export declare function toChatDto(s: DiscoveredSession, previewOverride?: string, usage?: ChatUsage | null, archived?: boolean, lastTurnCompletedAt?: string, lastSeen?: number, provenance?: RunProvenance | null, trigger?: ChatTriggerInfo | null, starred?: boolean, unread?: boolean, parent?: ChatParentRef | null): {
142
+ trigger?: ChatTriggerInfo | undefined;
143
+ parent?: ChatParentRef | undefined;
144
+ provenance?: RunProvenance | undefined;
145
+ contextTokens?: number | undefined;
146
+ contextLimit?: number | undefined;
147
+ inputTokens?: number | undefined;
148
+ outputTokens?: number | undefined;
149
+ cacheReadTokens?: number | undefined;
150
+ cacheCreationTokens?: number | undefined;
151
+ totalTokens?: number | undefined;
152
+ costUsd?: number | null | undefined;
153
+ unread?: boolean | undefined;
154
+ lastSeen?: number | undefined;
155
+ lastTurnCompletedAt?: string | undefined;
156
+ sessionId: string;
157
+ workingDirectory: string;
158
+ name: string;
159
+ updatedAt: string;
160
+ resumable: boolean;
161
+ preview: string | undefined;
162
+ archived: boolean;
163
+ starred: boolean;
164
+ };
165
+ /**
166
+ * Build the chat DTOs for a PROJECT's sessions, cleaning names polluted by the
167
+ * preload wrapper (issue #62). When a chat has no better name (no user rename,
168
+ * no Claude-generated summary) AND its preview is the injected `<project-context>`
169
+ * block, we read the untruncated first user message and strip the wrapper so the
170
+ * name reflects the user's actual request. Only preload chats trigger the extra
171
+ * (head-of-file) read; everything else maps straight through.
172
+ */
173
+ export declare function buildProjectChats(projectDir: string, sessions: DiscoveredSession[], usageOf?: (s: DiscoveredSession) => Promise<ChatUsage | null>, archivedOf?: (s: DiscoveredSession) => Promise<boolean>, lastTurnAt?: ReadonlyMap<string, string>, lastSeenOf?: (s: DiscoveredSession) => Promise<number>, provenanceOf?: (s: DiscoveredSession) => Promise<RunProvenance | undefined>, triggerOf?: (s: DiscoveredSession) => Promise<ChatTriggerInfo | undefined>, starredOf?: (s: DiscoveredSession) => Promise<boolean>, unreadOf?: (s: DiscoveredSession) => Promise<boolean>, parentOf?: (s: DiscoveredSession) => Promise<ChatParentRef | null>): Promise<{
174
+ trigger?: ChatTriggerInfo | undefined;
175
+ parent?: ChatParentRef | undefined;
176
+ provenance?: RunProvenance | undefined;
177
+ contextTokens?: number | undefined;
178
+ contextLimit?: number | undefined;
179
+ inputTokens?: number | undefined;
180
+ outputTokens?: number | undefined;
181
+ cacheReadTokens?: number | undefined;
182
+ cacheCreationTokens?: number | undefined;
183
+ totalTokens?: number | undefined;
184
+ costUsd?: number | null | undefined;
185
+ unread?: boolean | undefined;
186
+ lastSeen?: number | undefined;
187
+ lastTurnCompletedAt?: string | undefined;
188
+ sessionId: string;
189
+ workingDirectory: string;
190
+ name: string;
191
+ updatedAt: string;
192
+ resumable: boolean;
193
+ preview: string | undefined;
194
+ archived: boolean;
195
+ starred: boolean;
196
+ }[]>;
197
+ /**
198
+ * Build the `triggerOf` resolver for {@link buildProjectChats} (Epic T / T4): for
199
+ * a chat whose attributed agent is a `trigger-<slug>-<name>` agent, resolve its
200
+ * trigger's truthful-from-config capability descriptor for the floating capability
201
+ * banner. Returns `undefined` for every non-trigger chat (the common case).
202
+ *
203
+ * Built from the ALREADY-LOADED project record (no extra disk reads): the project's
204
+ * declared triggers are projected ONCE into an `agentName -> ChatTriggerInfo` map
205
+ * (via the same `toTriggerDto` → `toChatTriggerInfo` projection the trigger service
206
+ * uses, so the descriptor is truthful from config), and each trigger chat is an O(1)
207
+ * map lookup. The `TRIGGER_AGENT_PREFIX` fast-path skips even the lookup for the
208
+ * keeper chats that dominate the list.
209
+ */
210
+ export declare function makeTriggerResolver(project: Project): (s: DiscoveredSession) => Promise<ChatTriggerInfo | undefined>;
@@ -0,0 +1,308 @@
1
+ import { TRIGGER_AGENT_PREFIX, triggerAgentName } from "./herdctl.js";
2
+ import { getContextLimit, estimateCostUsdByModel } from "./models.js";
3
+ import { toTriggerDto } from "./triggers.js";
4
+ import { toChatTriggerInfo } from "./trigger-config.js";
5
+ import { readFirstUserText } from "./transcripts.js";
6
+ import { PRELOAD_CONTEXT_OPEN, stripPreloadWrapper } from "./preload.js";
7
+ import { stripAttachmentsWrapper, ATTACHMENTS_OPEN } from "./attachments-hint.js";
8
+ /** A session id safe to interpolate into a transcript file path (issue #329). */
9
+ export const SAFE_SESSION_ID = /^[A-Za-z0-9._-]+$/;
10
+ /**
11
+ * Which origins are, by definition, the ROOT of a chat tree. `spawned` is the
12
+ * only one that isn't: a human typed it, a schedule fired it, an event hook did,
13
+ * or it was IMPORTED from the user's own terminal history (#588) — none of those
14
+ * is created BY another chat, so none can acquire a parent.
15
+ *
16
+ * `adopted` belongs here for a stronger reason than the rest: the session ran
17
+ * OUTSIDE Paddock entirely, so there is no chat here that COULD be its parent.
18
+ * It is named explicitly rather than left to the `depth === 0` arm of
19
+ * {@link isRecordedRoot}, because depth is a value the import happens to stamp
20
+ * as 0 — if that ever changed, or a legacy marker carried a depth, the inference
21
+ * tier would go looking for a parent that cannot exist and file the chat under
22
+ * whichever chat happened to inject a prompt near it.
23
+ *
24
+ * A total `Record`, not a `Set`: a set of strings silently accepts a union that
25
+ * has grown past it, whereas a missing key here is a compile error — so adding
26
+ * an origin forces a deliberate root/child decision.
27
+ */
28
+ const ORIGIN_IS_ROOT = {
29
+ human: true,
30
+ scheduled: true,
31
+ hook: true,
32
+ adopted: true,
33
+ spawned: false,
34
+ };
35
+ /**
36
+ * Does this recorded provenance describe a chat that is a root by construction?
37
+ *
38
+ * `depth` counts spawn hops from the human/scheduled root, so depth 0 IS the root
39
+ * — and a root-origin marker says the same thing from the other direction. Either
40
+ * one means "this chat has no parent", as opposed to "this chat's parent was
41
+ * never recorded", which is the only case the inference tier exists to cover.
42
+ */
43
+ export function isRecordedRoot(p) {
44
+ return p.depth === 0 || ORIGIN_IS_ROOT[p.origin] === true;
45
+ }
46
+ /**
47
+ * Build the `parentOf` resolver for {@link buildProjectChats} — the chat-list
48
+ * parent edge, resolved in two tiers behind one override:
49
+ *
50
+ * 0. `ParentDetachStore` — the user explicitly DETACHED this chat (#508). Checked
51
+ * ahead of both tiers, because detach cannot be expressed by clearing an edge:
52
+ * most live edges are INFERRED (tier 2), so a cleared edge is simply re-derived
53
+ * on the next load. Detached ⇒ root, full stop.
54
+ * 1. `RunProvenance.parentSessionId` — the RECORDED edge, stamped at creation.
55
+ * 2. `MessageProvenanceStore.parentChat()` — a backfill for chats created before
56
+ * tier 1 existed, inferred from who injected the kickoff prompt.
57
+ *
58
+ * Tier 2 only applies to a chat whose parent is genuinely UNKNOWN. A chat with a
59
+ * recorded root marker (#491) is skipped outright: it isn't missing an edge, it
60
+ * has none. Without that guard, the documented report-back workflow re-parents a
61
+ * human's own chat — manager spawns child, child `send_message`s its report home,
62
+ * the manager now carries a `chat`-kind marker and infers its own child as its
63
+ * parent. Both edges then point at each other and `buildChatTree`'s cycle guard
64
+ * decides, per render, which of the two gets promoted to a root.
65
+ *
66
+ * All reads are in-memory sidecar lookups, so this is cheap enough to resolve
67
+ * inline for every row (unlike the usage ring, #116). A chat with neither tier is
68
+ * simply a root.
69
+ */
70
+ export function makeParentResolver(runProvenance, messageProvenance, projectSlug,
71
+ /**
72
+ * Has the user detached this chat from its parent (#508)? Optional so the many
73
+ * call sites that predate detach — and the unit tests — stay correct without it;
74
+ * omitted means "nothing is detached".
75
+ */
76
+ isDetached) {
77
+ return async (s) => {
78
+ // Tier 0: an explicit detach beats everything, recorded or inferred.
79
+ if (isDetached && (await isDetached(s.sessionId).catch(() => false)))
80
+ return null;
81
+ const p = await runProvenance.get(s.sessionId).catch(() => undefined);
82
+ // `parentProject` is a WORKSPACE KEY, and the root workspace's key is the
83
+ // empty string — a falsy test here would discard every edge whose parent is
84
+ // a root chat and silently fall through to the inference tier, rendering
85
+ // those chats as orphan roots. Only ABSENCE means "no recorded edge".
86
+ if (p?.parentSessionId && p.parentProject !== undefined)
87
+ return { project: p.parentProject, sessionId: p.parentSessionId };
88
+ // A recorded root never falls through to inference (#491).
89
+ if (p && isRecordedRoot(p))
90
+ return null;
91
+ const inferred = await messageProvenance.parentChat(s.sessionId).catch(() => null);
92
+ if (!inferred)
93
+ return null;
94
+ // Defensive: a chat can't parent itself. The tree builder guards cycles, but
95
+ // a self-edge here would silently drop the row from the roots.
96
+ if (inferred.sessionId === s.sessionId)
97
+ return null;
98
+ return { ...inferred, project: inferred.project || projectSlug };
99
+ };
100
+ }
101
+ /**
102
+ * Cap on how many chats one batch subtree operation may touch (#508). The client
103
+ * derives the set from a rendered subtree, so it is bounded by the chat list in
104
+ * practice; this is the "a hand-rolled request can't ask us to rewrite the whole
105
+ * sidecar" bound, not a UX limit.
106
+ */
107
+ export const BATCH_SESSIONS_MAX = 500;
108
+ /**
109
+ * Validate + normalise a batch route's `sessionIds` body field (#508).
110
+ *
111
+ * Returns the de-duplicated ids, or `null` when the body is unusable — which the
112
+ * routes turn into a 400. Deliberately ALL-OR-NOTHING: one malformed id fails the
113
+ * whole request rather than silently applying to the rest, because these
114
+ * operations are meant to keep a chat family in one state. Silently dropping an id
115
+ * would produce exactly the torn family the batch routes exist to prevent.
116
+ */
117
+ export function normalizeBatchSessionIds(raw) {
118
+ if (!Array.isArray(raw) || raw.length === 0 || raw.length > BATCH_SESSIONS_MAX)
119
+ return null;
120
+ const seen = new Set();
121
+ for (const v of raw) {
122
+ if (typeof v !== "string" || !SAFE_SESSION_ID.test(v))
123
+ return null;
124
+ seen.add(v);
125
+ }
126
+ return [...seen];
127
+ }
128
+ /** Claude Code's own preview cap (mirrors extractFirstMessagePreview). */
129
+ export const PREVIEW_MAX = 100;
130
+ /**
131
+ * Reserved read-state session id for the per-project, per-user "runs last seen"
132
+ * watermark (the since-last-visit digest, #268). A real Claude Code session id is
133
+ * a UUID (`/^[0-9a-f-]+$/`), so the double-underscore sentinel can never collide
134
+ * with one — the watermark keys cleanly alongside per-chat read-state.
135
+ */
136
+ export const RUNS_SEEN_SESSION = "__runs__";
137
+ /** Default + cap for the run-history page size. */
138
+ export const RUNS_LIMIT_DEFAULT = 100;
139
+ export const RUNS_LIMIT_MAX = 500;
140
+ /** Parse + clamp the `?limit=` query for the run-history endpoint. */
141
+ export function clampRunsLimit(raw) {
142
+ const n = raw === undefined ? RUNS_LIMIT_DEFAULT : Number.parseInt(raw, 10);
143
+ if (!Number.isFinite(n) || n <= 0)
144
+ return RUNS_LIMIT_DEFAULT;
145
+ return Math.min(n, RUNS_LIMIT_MAX);
146
+ }
147
+ /**
148
+ * Which chats the bulk usage endpoint computes rings for (issue #537). Usage has
149
+ * no stored counter — it is derived by streaming each transcript end to end — so
150
+ * scoping the request to what the sidebar actually renders is the difference
151
+ * between a few MB and a few hundred. `active` is the default because the
152
+ * Archived group is collapsed on open.
153
+ */
154
+ export const CHAT_USAGE_SCOPES = ["active", "archived", "all"];
155
+ /**
156
+ * Build the wire `ChatUsage` from a parsed {@link SessionTokenUsage} and the
157
+ * chat's model. Returns null when the transcript has no usage yet, so the ring
158
+ * simply hides. `totalTokens` is a headline "tokens this chat consumed" figure —
159
+ * output plus the (context-growing) input/cache reads — while `costUsd` prices
160
+ * each class separately (see {@link estimateCostUsd}).
161
+ */
162
+ export function toChatUsage(u, model) {
163
+ if (!u.hasData)
164
+ return null;
165
+ const totals = {
166
+ inputTokens: u.inputTotal,
167
+ outputTokens: u.outputTotal,
168
+ cacheReadTokens: u.cacheReadTotal,
169
+ cacheCreationTokens: u.cacheCreationTotal,
170
+ };
171
+ return {
172
+ contextTokens: u.contextTokens,
173
+ contextLimit: getContextLimit(model),
174
+ ...totals,
175
+ totalTokens: u.inputTotal + u.outputTotal + u.cacheReadTotal + u.cacheCreationTotal,
176
+ // Price per the model each turn actually ran on (u.byModel), not the passed
177
+ // project/chat default — a chat's turns can span models, so a single blended
178
+ // rate misprices (a Haiku chat billed at the Opus default would be 5× high).
179
+ costUsd: estimateCostUsdByModel(u.byModel),
180
+ };
181
+ }
182
+ export function toChatDto(s, previewOverride, usage, archived = false, lastTurnCompletedAt, lastSeen, provenance, trigger, starred = false, unread = false, parent) {
183
+ const preview = previewOverride ?? s.preview;
184
+ return {
185
+ sessionId: s.sessionId,
186
+ workingDirectory: s.workingDirectory,
187
+ name: s.customName ?? s.autoName ?? preview ?? s.sessionId.slice(0, 8),
188
+ updatedAt: s.mtime,
189
+ resumable: s.resumable,
190
+ preview,
191
+ // Whether this chat is filed away in the Archived section (#95). Always
192
+ // present so the client can partition the list without a fallback.
193
+ archived,
194
+ // Whether this chat is starred/pinned (#373) — sorts to the top of its
195
+ // population (active or archived). Always present, orthogonal to `archived`.
196
+ starred,
197
+ // ISO timestamp of the last turn the agent FINISHED (from job records, not
198
+ // mtime) — the unread signal (#160). Absent when no completed job record
199
+ // exists yet (session-mode chats, or a brand-new chat still on turn 1).
200
+ ...(lastTurnCompletedAt ? { lastTurnCompletedAt } : {}),
201
+ // Epoch-ms the user last viewed this chat (server-side read-state, #189) —
202
+ // the source of truth for the unread affordance, so it follows the user
203
+ // across devices. 0/absent means never seen on this instance.
204
+ ...(lastSeen ? { lastSeen } : {}),
205
+ // Whether the user MANUALLY flagged this chat unread (#458) — a per-user
206
+ // override on top of the derived unread signal, so a chat resurfaces its cue
207
+ // even after its last turn was seen. Only emitted when set (absent ⇒ false),
208
+ // keeping the payload compact like `lastSeen`.
209
+ ...(unread ? { unread: true } : {}),
210
+ // The context-window fill as of the session's last completed turn (for the
211
+ // per-chat usage ring) plus the chat's cumulative token totals and cost
212
+ // estimate (issue #152), so the list can render both without opening the
213
+ // chat. Only present when the transcript has usage data.
214
+ ...(usage ?? {}),
215
+ // How this chat was created (#267): A1's provenance marker (#261) — origin
216
+ // (human / scheduled / spawned / hook) + spawn depth — so the list can badge the
217
+ // "ran without me" cases. Absent when no marker was recorded (older chats,
218
+ // or ones created before A1). Human origin renders no badge (the default).
219
+ ...(provenance ? { provenance } : {}),
220
+ // The chat that created this one, so the list can nest it under its parent
221
+ // instead of only badging it "spawned". Recorded at creation on newer chats
222
+ // (RunProvenance.parentSessionId) and backfilled from the first injected-message
223
+ // sender on older ones. Absent for roots and for chats with no recoverable edge.
224
+ ...(parent ? { parent } : {}),
225
+ // For a TRIGGER chat (Epic T / T4): the truthful-from-config capability
226
+ // descriptor — trigger type (schedule/event/webhook) + WHEN it fires + granted
227
+ // tools — read from the same `trigger-<slug>-<name>` agent config herdctl
228
+ // enforces. Drives the floating capability banner atop the chat. Absent for
229
+ // non-trigger chats.
230
+ ...(trigger ? { trigger } : {}),
231
+ };
232
+ }
233
+ /**
234
+ * Build the chat DTOs for a PROJECT's sessions, cleaning names polluted by the
235
+ * preload wrapper (issue #62). When a chat has no better name (no user rename,
236
+ * no Claude-generated summary) AND its preview is the injected `<project-context>`
237
+ * block, we read the untruncated first user message and strip the wrapper so the
238
+ * name reflects the user's actual request. Only preload chats trigger the extra
239
+ * (head-of-file) read; everything else maps straight through.
240
+ */
241
+ export async function buildProjectChats(projectDir, sessions, usageOf, archivedOf, lastTurnAt, lastSeenOf, provenanceOf, triggerOf, starredOf, unreadOf, parentOf) {
242
+ return Promise.all(sessions.map(async (s) => {
243
+ // Resolve the usage ring, archived flag, read-state, provenance, trigger, star, unread, name.
244
+ const usage = usageOf ? await usageOf(s).catch(() => null) : null;
245
+ const archived = archivedOf ? await archivedOf(s).catch(() => false) : false;
246
+ const turnAt = lastTurnAt?.get(s.sessionId);
247
+ const lastSeen = lastSeenOf ? await lastSeenOf(s).catch(() => 0) : 0;
248
+ const provenance = provenanceOf ? await provenanceOf(s).catch(() => null) : null;
249
+ const trigger = triggerOf ? await triggerOf(s).catch(() => undefined) : undefined;
250
+ const starred = starredOf ? await starredOf(s).catch(() => false) : false;
251
+ const unread = unreadOf ? await unreadOf(s).catch(() => false) : false;
252
+ const parent = parentOf ? await parentOf(s).catch(() => null) : null;
253
+ // A name polluted by a machine-prepended wrapper: the preload context block
254
+ // (#1) and/or the composer-attachment block (#328). Either makes the raw
255
+ // first message a poor display name, so recover the real request below.
256
+ //
257
+ // `autoName` has to be tested, not merely checked for absence. @herdctl/core
258
+ // used to leave it undefined for a transcript with no title entry — which is
259
+ // nearly every CLI transcript — so "no autoName" was a reliable proxy for
260
+ // "the preview is all we have". It now falls back to the first user message
261
+ // itself (custom-title → ai-title → summary → preview), so a preload chat
262
+ // arrives with `autoName` ALREADY set to the `<project-context>` block. Kept
263
+ // as an absence check, this whole branch became unreachable and every
264
+ // preload chat was titled with the injected overview.
265
+ const isWrapped = (v) => v !== undefined && (v.startsWith(PRELOAD_CONTEXT_OPEN) || v.startsWith(ATTACHMENTS_OPEN));
266
+ // A polluted autoName is worse than none: it would beat the cleaned preview
267
+ // in `toChatDto`'s name precedence, so drop it rather than compete with it.
268
+ const session = isWrapped(s.autoName) ? { ...s, autoName: undefined } : s;
269
+ const pollutedPreview = !session.customName &&
270
+ !session.autoName &&
271
+ (isWrapped(s.preview) || isWrapped(s.autoName));
272
+ if (!pollutedPreview)
273
+ return toChatDto(session, undefined, usage, archived, turnAt, lastSeen, provenance, trigger, starred, unread, parent);
274
+ const full = await readFirstUserText(projectDir, s.sessionId).catch(() => undefined);
275
+ // Strip preload FIRST (it wraps the whole thing), then the attachment block
276
+ // nested inside it, leaving just the user's typed request.
277
+ const cleaned = stripAttachmentsWrapper(stripPreloadWrapper(full ?? s.preview ?? s.autoName ?? "")).trim();
278
+ // couldn't recover
279
+ if (!cleaned)
280
+ return toChatDto(session, undefined, usage, archived, turnAt, lastSeen, provenance, trigger, starred, unread, parent);
281
+ const preview = cleaned.length > PREVIEW_MAX ? `${cleaned.slice(0, PREVIEW_MAX)}...` : cleaned;
282
+ return toChatDto(session, preview, usage, archived, turnAt, lastSeen, provenance, trigger, starred, unread, parent);
283
+ }));
284
+ }
285
+ /**
286
+ * Build the `triggerOf` resolver for {@link buildProjectChats} (Epic T / T4): for
287
+ * a chat whose attributed agent is a `trigger-<slug>-<name>` agent, resolve its
288
+ * trigger's truthful-from-config capability descriptor for the floating capability
289
+ * banner. Returns `undefined` for every non-trigger chat (the common case).
290
+ *
291
+ * Built from the ALREADY-LOADED project record (no extra disk reads): the project's
292
+ * declared triggers are projected ONCE into an `agentName -> ChatTriggerInfo` map
293
+ * (via the same `toTriggerDto` → `toChatTriggerInfo` projection the trigger service
294
+ * uses, so the descriptor is truthful from config), and each trigger chat is an O(1)
295
+ * map lookup. The `TRIGGER_AGENT_PREFIX` fast-path skips even the lookup for the
296
+ * keeper chats that dominate the list.
297
+ */
298
+ export function makeTriggerResolver(project) {
299
+ const byAgentName = new Map();
300
+ for (const [name, trigger] of Object.entries(project.triggers ?? {})) {
301
+ byAgentName.set(triggerAgentName(project.slug, name), toChatTriggerInfo(toTriggerDto(project.slug, name, trigger)));
302
+ }
303
+ return async (s) => {
304
+ if (!s.agentName || !s.agentName.startsWith(TRIGGER_AGENT_PREFIX))
305
+ return undefined;
306
+ return byAgentName.get(s.agentName);
307
+ };
308
+ }
@@ -0,0 +1,21 @@
1
+ interface Args {
2
+ project?: string;
3
+ from?: string;
4
+ move: boolean;
5
+ dryRun: boolean;
6
+ dataDir?: string;
7
+ json: boolean;
8
+ help: boolean;
9
+ }
10
+ /**
11
+ * Parse argv. Hand-rolled because this is the repo's FIRST script taking flags
12
+ * (`pm` parses its own, in CommonJS) — there is no convention to follow and no
13
+ * argument parser in the dependency tree, and adding one for six flags would be
14
+ * the larger change.
15
+ *
16
+ * `--project ""` must survive: the root workspace's key is the empty string, so
17
+ * a "missing value" check written as truthiness would reject the one workspace
18
+ * that always exists.
19
+ */
20
+ export declare function parseArgs(argv: string[]): Args;
21
+ export {};