@cursor/july 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (623) hide show
  1. package/AGENTS.md +290 -0
  2. package/LICENSE.md +3 -0
  3. package/README.md +1233 -0
  4. package/dist/ab.d.ts +290 -0
  5. package/dist/ab.d.ts.map +1 -0
  6. package/dist/ab.js +386 -0
  7. package/dist/bin/agent-serve.d.ts +15 -0
  8. package/dist/bin/agent-serve.d.ts.map +1 -0
  9. package/dist/bin/agent-serve.js +754 -0
  10. package/dist/channels/github/api.d.ts +101 -0
  11. package/dist/channels/github/api.d.ts.map +1 -0
  12. package/dist/channels/github/api.js +229 -0
  13. package/dist/channels/github/auth.d.ts +67 -0
  14. package/dist/channels/github/auth.d.ts.map +1 -0
  15. package/dist/channels/github/auth.js +239 -0
  16. package/dist/channels/github/binding.d.ts +73 -0
  17. package/dist/channels/github/binding.d.ts.map +1 -0
  18. package/dist/channels/github/binding.js +114 -0
  19. package/dist/channels/github/defaults.d.ts +24 -0
  20. package/dist/channels/github/defaults.d.ts.map +1 -0
  21. package/dist/channels/github/defaults.js +100 -0
  22. package/dist/channels/github/events.d.ts +18 -0
  23. package/dist/channels/github/events.d.ts.map +1 -0
  24. package/dist/channels/github/events.js +59 -0
  25. package/dist/channels/github/forward.d.ts +137 -0
  26. package/dist/channels/github/forward.d.ts.map +1 -0
  27. package/dist/channels/github/forward.js +271 -0
  28. package/dist/channels/github/github-channel.d.ts +127 -0
  29. package/dist/channels/github/github-channel.d.ts.map +1 -0
  30. package/dist/channels/github/github-channel.js +1688 -0
  31. package/dist/channels/github/index.d.ts +52 -0
  32. package/dist/channels/github/index.d.ts.map +1 -0
  33. package/dist/channels/github/index.js +51 -0
  34. package/dist/channels/github/replay.d.ts +115 -0
  35. package/dist/channels/github/replay.d.ts.map +1 -0
  36. package/dist/channels/github/replay.js +282 -0
  37. package/dist/channels/github/state.d.ts +26 -0
  38. package/dist/channels/github/state.d.ts.map +1 -0
  39. package/dist/channels/github/state.js +50 -0
  40. package/dist/channels/github/types.d.ts +385 -0
  41. package/dist/channels/github/types.d.ts.map +1 -0
  42. package/dist/channels/github/types.js +1 -0
  43. package/dist/channels/slack/api.d.ts +117 -0
  44. package/dist/channels/slack/api.d.ts.map +1 -0
  45. package/dist/channels/slack/api.js +437 -0
  46. package/dist/channels/slack/approvals.d.ts +32 -0
  47. package/dist/channels/slack/approvals.d.ts.map +1 -0
  48. package/dist/channels/slack/approvals.js +157 -0
  49. package/dist/channels/slack/assistant.d.ts +23 -0
  50. package/dist/channels/slack/assistant.d.ts.map +1 -0
  51. package/dist/channels/slack/assistant.js +67 -0
  52. package/dist/channels/slack/auth.d.ts +40 -0
  53. package/dist/channels/slack/auth.d.ts.map +1 -0
  54. package/dist/channels/slack/auth.js +71 -0
  55. package/dist/channels/slack/channel-watch.d.ts +46 -0
  56. package/dist/channels/slack/channel-watch.d.ts.map +1 -0
  57. package/dist/channels/slack/channel-watch.js +180 -0
  58. package/dist/channels/slack/constants.d.ts +17 -0
  59. package/dist/channels/slack/constants.d.ts.map +1 -0
  60. package/dist/channels/slack/constants.js +27 -0
  61. package/dist/channels/slack/credentials.d.ts +35 -0
  62. package/dist/channels/slack/credentials.d.ts.map +1 -0
  63. package/dist/channels/slack/credentials.js +88 -0
  64. package/dist/channels/slack/cursor-account.d.ts +76 -0
  65. package/dist/channels/slack/cursor-account.d.ts.map +1 -0
  66. package/dist/channels/slack/cursor-account.js +88 -0
  67. package/dist/channels/slack/defaults.d.ts +32 -0
  68. package/dist/channels/slack/defaults.d.ts.map +1 -0
  69. package/dist/channels/slack/defaults.js +380 -0
  70. package/dist/channels/slack/dispatch.d.ts +43 -0
  71. package/dist/channels/slack/dispatch.d.ts.map +1 -0
  72. package/dist/channels/slack/dispatch.js +185 -0
  73. package/dist/channels/slack/doctor.d.ts +24 -0
  74. package/dist/channels/slack/doctor.d.ts.map +1 -0
  75. package/dist/channels/slack/doctor.js +115 -0
  76. package/dist/channels/slack/event-id-dedupe.d.ts +10 -0
  77. package/dist/channels/slack/event-id-dedupe.d.ts.map +1 -0
  78. package/dist/channels/slack/event-id-dedupe.js +23 -0
  79. package/dist/channels/slack/inbound.d.ts +41 -0
  80. package/dist/channels/slack/inbound.d.ts.map +1 -0
  81. package/dist/channels/slack/inbound.js +166 -0
  82. package/dist/channels/slack/index.d.ts +45 -0
  83. package/dist/channels/slack/index.d.ts.map +1 -0
  84. package/dist/channels/slack/index.js +44 -0
  85. package/dist/channels/slack/init.d.ts +32 -0
  86. package/dist/channels/slack/init.d.ts.map +1 -0
  87. package/dist/channels/slack/init.js +82 -0
  88. package/dist/channels/slack/interactive.d.ts +19 -0
  89. package/dist/channels/slack/interactive.d.ts.map +1 -0
  90. package/dist/channels/slack/interactive.js +142 -0
  91. package/dist/channels/slack/links.d.ts +10 -0
  92. package/dist/channels/slack/links.d.ts.map +1 -0
  93. package/dist/channels/slack/links.js +19 -0
  94. package/dist/channels/slack/live-delivery.d.ts +75 -0
  95. package/dist/channels/slack/live-delivery.d.ts.map +1 -0
  96. package/dist/channels/slack/live-delivery.js +143 -0
  97. package/dist/channels/slack/log.d.ts +25 -0
  98. package/dist/channels/slack/log.d.ts.map +1 -0
  99. package/dist/channels/slack/log.js +47 -0
  100. package/dist/channels/slack/manifest.d.ts +57 -0
  101. package/dist/channels/slack/manifest.d.ts.map +1 -0
  102. package/dist/channels/slack/manifest.js +110 -0
  103. package/dist/channels/slack/pr-ref.d.ts +18 -0
  104. package/dist/channels/slack/pr-ref.d.ts.map +1 -0
  105. package/dist/channels/slack/pr-ref.js +36 -0
  106. package/dist/channels/slack/setup.d.ts +14 -0
  107. package/dist/channels/slack/setup.d.ts.map +1 -0
  108. package/dist/channels/slack/setup.js +86 -0
  109. package/dist/channels/slack/slack-channel.d.ts +15 -0
  110. package/dist/channels/slack/slack-channel.d.ts.map +1 -0
  111. package/dist/channels/slack/slack-channel.js +281 -0
  112. package/dist/channels/slack/socket-mode.d.ts +26 -0
  113. package/dist/channels/slack/socket-mode.d.ts.map +1 -0
  114. package/dist/channels/slack/socket-mode.js +116 -0
  115. package/dist/channels/slack/stream.d.ts +26 -0
  116. package/dist/channels/slack/stream.d.ts.map +1 -0
  117. package/dist/channels/slack/stream.js +39 -0
  118. package/dist/channels/slack/tool-approval-events.d.ts +33 -0
  119. package/dist/channels/slack/tool-approval-events.d.ts.map +1 -0
  120. package/dist/channels/slack/tool-approval-events.js +129 -0
  121. package/dist/channels/slack/types.d.ts +326 -0
  122. package/dist/channels/slack/types.d.ts.map +1 -0
  123. package/dist/channels/slack/types.js +4 -0
  124. package/dist/channels.d.ts +116 -0
  125. package/dist/channels.d.ts.map +1 -0
  126. package/dist/channels.js +294 -0
  127. package/dist/connections.d.ts +75 -0
  128. package/dist/connections.d.ts.map +1 -0
  129. package/dist/connections.js +75 -0
  130. package/dist/docs/404.html +22 -0
  131. package/dist/docs/ab.html +87 -0
  132. package/dist/docs/assets/ab.md.COdXkces.js +63 -0
  133. package/dist/docs/assets/ab.md.COdXkces.lean.js +1 -0
  134. package/dist/docs/assets/app.DqfFEmJd.js +1 -0
  135. package/dist/docs/assets/building-with-agents.md.CJCtZCyi.js +13 -0
  136. package/dist/docs/assets/building-with-agents.md.CJCtZCyi.lean.js +1 -0
  137. package/dist/docs/assets/chunks/@localSearchIndexroot.CcVk1uKq.js +1 -0
  138. package/dist/docs/assets/chunks/VPLocalSearchBox.BaLEdS15.js +9 -0
  139. package/dist/docs/assets/chunks/framework.CAZyNGu9.js +19 -0
  140. package/dist/docs/assets/chunks/theme.CZRvu_0q.js +2 -0
  141. package/dist/docs/assets/concepts.md.Cfb9b-k1.js +4 -0
  142. package/dist/docs/assets/concepts.md.Cfb9b-k1.lean.js +1 -0
  143. package/dist/docs/assets/deployment.md.Dx1TYNk5.js +14 -0
  144. package/dist/docs/assets/deployment.md.Dx1TYNk5.lean.js +1 -0
  145. package/dist/docs/assets/evals.md.DPZ_MAnI.js +85 -0
  146. package/dist/docs/assets/evals.md.DPZ_MAnI.lean.js +1 -0
  147. package/dist/docs/assets/guides_agent-to-agent.md.CrtrsySy.js +10 -0
  148. package/dist/docs/assets/guides_agent-to-agent.md.CrtrsySy.lean.js +1 -0
  149. package/dist/docs/assets/guides_cloud-runtime.md.CYlNMTNp.js +9 -0
  150. package/dist/docs/assets/guides_cloud-runtime.md.CYlNMTNp.lean.js +1 -0
  151. package/dist/docs/assets/guides_github.md.DwbKhCeS.js +20 -0
  152. package/dist/docs/assets/guides_github.md.DwbKhCeS.lean.js +1 -0
  153. package/dist/docs/assets/guides_human-in-the-loop.md.Dvuctx7s.js +28 -0
  154. package/dist/docs/assets/guides_human-in-the-loop.md.Dvuctx7s.lean.js +1 -0
  155. package/dist/docs/assets/guides_slack.md.bv41fHfW.js +37 -0
  156. package/dist/docs/assets/guides_slack.md.bv41fHfW.lean.js +1 -0
  157. package/dist/docs/assets/guides_webhooks.md.hFTik3lf.js +50 -0
  158. package/dist/docs/assets/guides_webhooks.md.hFTik3lf.lean.js +1 -0
  159. package/dist/docs/assets/hillclimbing.md.DHNast08.js +4 -0
  160. package/dist/docs/assets/hillclimbing.md.DHNast08.lean.js +1 -0
  161. package/dist/docs/assets/index.md.BPKcj5AI.js +20 -0
  162. package/dist/docs/assets/index.md.BPKcj5AI.lean.js +1 -0
  163. package/dist/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  164. package/dist/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  165. package/dist/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  166. package/dist/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  167. package/dist/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  168. package/dist/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  169. package/dist/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  170. package/dist/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  171. package/dist/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  172. package/dist/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  173. package/dist/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  174. package/dist/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  175. package/dist/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  176. package/dist/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  177. package/dist/docs/assets/quickstart.md.tVPiGK_L.js +35 -0
  178. package/dist/docs/assets/quickstart.md.tVPiGK_L.lean.js +1 -0
  179. package/dist/docs/assets/reference_agent-config.md.Bpd7HQwf.js +34 -0
  180. package/dist/docs/assets/reference_agent-config.md.Bpd7HQwf.lean.js +1 -0
  181. package/dist/docs/assets/reference_channels.md.D7JTR03W.js +53 -0
  182. package/dist/docs/assets/reference_channels.md.D7JTR03W.lean.js +1 -0
  183. package/dist/docs/assets/reference_cli.md.DA730zCu.js +33 -0
  184. package/dist/docs/assets/reference_cli.md.DA730zCu.lean.js +1 -0
  185. package/dist/docs/assets/reference_connections.md.C3vNH_DE.js +26 -0
  186. package/dist/docs/assets/reference_connections.md.C3vNH_DE.lean.js +1 -0
  187. package/dist/docs/assets/reference_hooks.md.BCEc3MyM.js +12 -0
  188. package/dist/docs/assets/reference_hooks.md.BCEc3MyM.lean.js +1 -0
  189. package/dist/docs/assets/reference_http-api.md.DBAahtdz.js +11 -0
  190. package/dist/docs/assets/reference_http-api.md.DBAahtdz.lean.js +1 -0
  191. package/dist/docs/assets/reference_instructions.md.BC05LEQ8.js +12 -0
  192. package/dist/docs/assets/reference_instructions.md.BC05LEQ8.lean.js +1 -0
  193. package/dist/docs/assets/reference_playground.md.CPZhfYaO.js +1 -0
  194. package/dist/docs/assets/reference_playground.md.CPZhfYaO.lean.js +1 -0
  195. package/dist/docs/assets/reference_project-layout.md.Bd_CKtNS.js +31 -0
  196. package/dist/docs/assets/reference_project-layout.md.Bd_CKtNS.lean.js +1 -0
  197. package/dist/docs/assets/reference_schedules.md.D7qijxLk.js +82 -0
  198. package/dist/docs/assets/reference_schedules.md.D7qijxLk.lean.js +1 -0
  199. package/dist/docs/assets/reference_sessions.md.DdjWabnN.js +8 -0
  200. package/dist/docs/assets/reference_sessions.md.DdjWabnN.lean.js +1 -0
  201. package/dist/docs/assets/reference_skills.md.VQnlBT3Q.js +15 -0
  202. package/dist/docs/assets/reference_skills.md.VQnlBT3Q.lean.js +1 -0
  203. package/dist/docs/assets/reference_subagents.md.CIRAVcPK.js +10 -0
  204. package/dist/docs/assets/reference_subagents.md.CIRAVcPK.lean.js +1 -0
  205. package/dist/docs/assets/reference_tools.md.DF5kwlt0.js +46 -0
  206. package/dist/docs/assets/reference_tools.md.DF5kwlt0.lean.js +1 -0
  207. package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.js +1 -0
  208. package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.lean.js +1 -0
  209. package/dist/docs/assets/style.Bj-IXa5W.css +1 -0
  210. package/dist/docs/assets/troubleshooting.md.DkovjS7h.js +1 -0
  211. package/dist/docs/assets/troubleshooting.md.DkovjS7h.lean.js +1 -0
  212. package/dist/docs/building-with-agents.html +37 -0
  213. package/dist/docs/concepts.html +28 -0
  214. package/dist/docs/deployment.html +38 -0
  215. package/dist/docs/evals.html +109 -0
  216. package/dist/docs/guides/agent-to-agent.html +34 -0
  217. package/dist/docs/guides/cloud-runtime.html +33 -0
  218. package/dist/docs/guides/github.html +44 -0
  219. package/dist/docs/guides/human-in-the-loop.html +52 -0
  220. package/dist/docs/guides/slack.html +61 -0
  221. package/dist/docs/guides/webhooks.html +74 -0
  222. package/dist/docs/hashmap.json +1 -0
  223. package/dist/docs/hillclimbing.html +28 -0
  224. package/dist/docs/index.html +44 -0
  225. package/dist/docs/quickstart.html +59 -0
  226. package/dist/docs/reference/agent-config.html +58 -0
  227. package/dist/docs/reference/channels.html +77 -0
  228. package/dist/docs/reference/cli.html +57 -0
  229. package/dist/docs/reference/connections.html +50 -0
  230. package/dist/docs/reference/hooks.html +36 -0
  231. package/dist/docs/reference/http-api.html +35 -0
  232. package/dist/docs/reference/instructions.html +36 -0
  233. package/dist/docs/reference/playground.html +25 -0
  234. package/dist/docs/reference/project-layout.html +55 -0
  235. package/dist/docs/reference/schedules.html +106 -0
  236. package/dist/docs/reference/sessions.html +32 -0
  237. package/dist/docs/reference/skills.html +39 -0
  238. package/dist/docs/reference/subagents.html +34 -0
  239. package/dist/docs/reference/tools.html +70 -0
  240. package/dist/docs/scaffolding-agents.html +25 -0
  241. package/dist/docs/troubleshooting.html +25 -0
  242. package/dist/docs/vp-icons.css +0 -0
  243. package/dist/evals.d.ts +312 -0
  244. package/dist/evals.d.ts.map +1 -0
  245. package/dist/evals.js +254 -0
  246. package/dist/hooks.d.ts +23 -0
  247. package/dist/hooks.d.ts.map +1 -0
  248. package/dist/hooks.js +23 -0
  249. package/dist/index.d.ts +71 -0
  250. package/dist/index.d.ts.map +1 -0
  251. package/dist/index.js +101 -0
  252. package/dist/internal/ab-collector.d.ts +42 -0
  253. package/dist/internal/ab-collector.d.ts.map +1 -0
  254. package/dist/internal/ab-collector.js +153 -0
  255. package/dist/internal/ab-fold.d.ts +36 -0
  256. package/dist/internal/ab-fold.d.ts.map +1 -0
  257. package/dist/internal/ab-fold.js +171 -0
  258. package/dist/internal/ab-snapshot.d.ts +70 -0
  259. package/dist/internal/ab-snapshot.d.ts.map +1 -0
  260. package/dist/internal/ab-snapshot.js +208 -0
  261. package/dist/internal/approval-gate.d.ts +34 -0
  262. package/dist/internal/approval-gate.d.ts.map +1 -0
  263. package/dist/internal/approval-gate.js +85 -0
  264. package/dist/internal/auth.d.ts +8 -0
  265. package/dist/internal/auth.d.ts.map +1 -0
  266. package/dist/internal/auth.js +27 -0
  267. package/dist/internal/authored-alias-hooks.d.ts +31 -0
  268. package/dist/internal/authored-alias-hooks.d.ts.map +1 -0
  269. package/dist/internal/authored-alias-hooks.js +43 -0
  270. package/dist/internal/authored-loaders.d.ts +25 -0
  271. package/dist/internal/authored-loaders.d.ts.map +1 -0
  272. package/dist/internal/authored-loaders.js +129 -0
  273. package/dist/internal/bounded-int.d.ts +11 -0
  274. package/dist/internal/bounded-int.d.ts.map +1 -0
  275. package/dist/internal/bounded-int.js +13 -0
  276. package/dist/internal/brand.d.ts +6 -0
  277. package/dist/internal/brand.d.ts.map +1 -0
  278. package/dist/internal/brand.js +17 -0
  279. package/dist/internal/chat-client.d.ts +19 -0
  280. package/dist/internal/chat-client.d.ts.map +1 -0
  281. package/dist/internal/chat-client.js +327 -0
  282. package/dist/internal/cli-ax.d.ts +58 -0
  283. package/dist/internal/cli-ax.d.ts.map +1 -0
  284. package/dist/internal/cli-ax.js +345 -0
  285. package/dist/internal/cli-cursor.d.ts +20 -0
  286. package/dist/internal/cli-cursor.d.ts.map +1 -0
  287. package/dist/internal/cli-cursor.js +227 -0
  288. package/dist/internal/cli-github.d.ts +47 -0
  289. package/dist/internal/cli-github.d.ts.map +1 -0
  290. package/dist/internal/cli-github.js +644 -0
  291. package/dist/internal/cli-slack.d.ts +18 -0
  292. package/dist/internal/cli-slack.d.ts.map +1 -0
  293. package/dist/internal/cli-slack.js +144 -0
  294. package/dist/internal/cloud-merge.d.ts +23 -0
  295. package/dist/internal/cloud-merge.d.ts.map +1 -0
  296. package/dist/internal/cloud-merge.js +28 -0
  297. package/dist/internal/cron.d.ts +17 -0
  298. package/dist/internal/cron.d.ts.map +1 -0
  299. package/dist/internal/cron.js +60 -0
  300. package/dist/internal/cursor/account-mcp.d.ts +95 -0
  301. package/dist/internal/cursor/account-mcp.d.ts.map +1 -0
  302. package/dist/internal/cursor/account-mcp.js +342 -0
  303. package/dist/internal/cursor/backend-client.d.ts +118 -0
  304. package/dist/internal/cursor/backend-client.d.ts.map +1 -0
  305. package/dist/internal/cursor/backend-client.js +477 -0
  306. package/dist/internal/cursor/credentials.d.ts +112 -0
  307. package/dist/internal/cursor/credentials.d.ts.map +1 -0
  308. package/dist/internal/cursor/credentials.js +329 -0
  309. package/dist/internal/cursor-event-relay.d.ts +111 -0
  310. package/dist/internal/cursor-event-relay.d.ts.map +1 -0
  311. package/dist/internal/cursor-event-relay.js +181 -0
  312. package/dist/internal/cursor-relay-core.d.ts +113 -0
  313. package/dist/internal/cursor-relay-core.d.ts.map +1 -0
  314. package/dist/internal/cursor-relay-core.js +364 -0
  315. package/dist/internal/cursor-slack-relay.d.ts +94 -0
  316. package/dist/internal/cursor-slack-relay.d.ts.map +1 -0
  317. package/dist/internal/cursor-slack-relay.js +171 -0
  318. package/dist/internal/describe-error.d.ts +3 -0
  319. package/dist/internal/describe-error.d.ts.map +1 -0
  320. package/dist/internal/describe-error.js +4 -0
  321. package/dist/internal/dev-mode.d.ts +13 -0
  322. package/dist/internal/dev-mode.d.ts.map +1 -0
  323. package/dist/internal/dev-mode.js +17 -0
  324. package/dist/internal/discovery.d.ts +55 -0
  325. package/dist/internal/discovery.d.ts.map +1 -0
  326. package/dist/internal/discovery.js +1427 -0
  327. package/dist/internal/distribution.d.ts +47 -0
  328. package/dist/internal/distribution.d.ts.map +1 -0
  329. package/dist/internal/distribution.js +82 -0
  330. package/dist/internal/docs-site.d.ts +28 -0
  331. package/dist/internal/docs-site.d.ts.map +1 -0
  332. package/dist/internal/docs-site.js +169 -0
  333. package/dist/internal/duration.d.ts +8 -0
  334. package/dist/internal/duration.d.ts.map +1 -0
  335. package/dist/internal/duration.js +35 -0
  336. package/dist/internal/eval-run-store.d.ts +81 -0
  337. package/dist/internal/eval-run-store.d.ts.map +1 -0
  338. package/dist/internal/eval-run-store.js +396 -0
  339. package/dist/internal/eval-runner.d.ts +76 -0
  340. package/dist/internal/eval-runner.d.ts.map +1 -0
  341. package/dist/internal/eval-runner.js +336 -0
  342. package/dist/internal/event-log.d.ts +51 -0
  343. package/dist/internal/event-log.d.ts.map +1 -0
  344. package/dist/internal/event-log.js +167 -0
  345. package/dist/internal/event-mapper.d.ts +46 -0
  346. package/dist/internal/event-mapper.d.ts.map +1 -0
  347. package/dist/internal/event-mapper.js +226 -0
  348. package/dist/internal/frontmatter.d.ts +12 -0
  349. package/dist/internal/frontmatter.d.ts.map +1 -0
  350. package/dist/internal/frontmatter.js +61 -0
  351. package/dist/internal/github-fanout.d.ts +32 -0
  352. package/dist/internal/github-fanout.d.ts.map +1 -0
  353. package/dist/internal/github-fanout.js +122 -0
  354. package/dist/internal/handleAgentServeTrigger.d.ts +59 -0
  355. package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -0
  356. package/dist/internal/handleAgentServeTrigger.js +115 -0
  357. package/dist/internal/host-platforms.d.ts +40 -0
  358. package/dist/internal/host-platforms.d.ts.map +1 -0
  359. package/dist/internal/host-platforms.js +113 -0
  360. package/dist/internal/http-channel.d.ts +54 -0
  361. package/dist/internal/http-channel.d.ts.map +1 -0
  362. package/dist/internal/http-channel.js +382 -0
  363. package/dist/internal/ids.d.ts +19 -0
  364. package/dist/internal/ids.d.ts.map +1 -0
  365. package/dist/internal/ids.js +34 -0
  366. package/dist/internal/init-project.d.ts +22 -0
  367. package/dist/internal/init-project.d.ts.map +1 -0
  368. package/dist/internal/init-project.js +125 -0
  369. package/dist/internal/json-dir-store.d.ts +32 -0
  370. package/dist/internal/json-dir-store.d.ts.map +1 -0
  371. package/dist/internal/json-dir-store.js +100 -0
  372. package/dist/internal/json-schema.d.ts +18 -0
  373. package/dist/internal/json-schema.d.ts.map +1 -0
  374. package/dist/internal/json-schema.js +53 -0
  375. package/dist/internal/log-ring.d.ts +23 -0
  376. package/dist/internal/log-ring.d.ts.map +1 -0
  377. package/dist/internal/log-ring.js +42 -0
  378. package/dist/internal/map-pool.d.ts +6 -0
  379. package/dist/internal/map-pool.d.ts.map +1 -0
  380. package/dist/internal/map-pool.js +41 -0
  381. package/dist/internal/mcp-endpoint.d.ts +59 -0
  382. package/dist/internal/mcp-endpoint.d.ts.map +1 -0
  383. package/dist/internal/mcp-endpoint.js +355 -0
  384. package/dist/internal/mcp-host.d.ts +28 -0
  385. package/dist/internal/mcp-host.d.ts.map +1 -0
  386. package/dist/internal/mcp-host.js +162 -0
  387. package/dist/internal/net.d.ts +5 -0
  388. package/dist/internal/net.d.ts.map +1 -0
  389. package/dist/internal/net.js +30 -0
  390. package/dist/internal/peer-connections.d.ts +44 -0
  391. package/dist/internal/peer-connections.d.ts.map +1 -0
  392. package/dist/internal/peer-connections.js +67 -0
  393. package/dist/internal/playground/static.d.ts +35 -0
  394. package/dist/internal/playground/static.d.ts.map +1 -0
  395. package/dist/internal/playground/static.js +162 -0
  396. package/dist/internal/playground/toolchain.d.ts +57 -0
  397. package/dist/internal/playground/toolchain.d.ts.map +1 -0
  398. package/dist/internal/playground/toolchain.js +226 -0
  399. package/dist/internal/reminder-runner.d.ts +62 -0
  400. package/dist/internal/reminder-runner.d.ts.map +1 -0
  401. package/dist/internal/reminder-runner.js +582 -0
  402. package/dist/internal/reminder-schedule.d.ts +22 -0
  403. package/dist/internal/reminder-schedule.d.ts.map +1 -0
  404. package/dist/internal/reminder-schedule.js +82 -0
  405. package/dist/internal/reminder-store.d.ts +58 -0
  406. package/dist/internal/reminder-store.d.ts.map +1 -0
  407. package/dist/internal/reminder-store.js +106 -0
  408. package/dist/internal/resolved-connections.d.ts +41 -0
  409. package/dist/internal/resolved-connections.d.ts.map +1 -0
  410. package/dist/internal/resolved-connections.js +73 -0
  411. package/dist/internal/review-comments.d.ts +50 -0
  412. package/dist/internal/review-comments.d.ts.map +1 -0
  413. package/dist/internal/review-comments.js +194 -0
  414. package/dist/internal/router.d.ts +33 -0
  415. package/dist/internal/router.d.ts.map +1 -0
  416. package/dist/internal/router.js +93 -0
  417. package/dist/internal/run-client.d.ts +49 -0
  418. package/dist/internal/run-client.d.ts.map +1 -0
  419. package/dist/internal/run-client.js +208 -0
  420. package/dist/internal/schedule-runner.d.ts +31 -0
  421. package/dist/internal/schedule-runner.d.ts.map +1 -0
  422. package/dist/internal/schedule-runner.js +126 -0
  423. package/dist/internal/sdk-runner.d.ts +41 -0
  424. package/dist/internal/sdk-runner.d.ts.map +1 -0
  425. package/dist/internal/sdk-runner.js +221 -0
  426. package/dist/internal/server.d.ts +25 -0
  427. package/dist/internal/server.d.ts.map +1 -0
  428. package/dist/internal/server.js +1374 -0
  429. package/dist/internal/session-engine.d.ts +287 -0
  430. package/dist/internal/session-engine.d.ts.map +1 -0
  431. package/dist/internal/session-engine.js +1377 -0
  432. package/dist/internal/session-store.d.ts +29 -0
  433. package/dist/internal/session-store.d.ts.map +1 -0
  434. package/dist/internal/session-store.js +140 -0
  435. package/dist/internal/session-urls.d.ts +10 -0
  436. package/dist/internal/session-urls.d.ts.map +1 -0
  437. package/dist/internal/session-urls.js +25 -0
  438. package/dist/internal/stream-progress.d.ts +14 -0
  439. package/dist/internal/stream-progress.d.ts.map +1 -0
  440. package/dist/internal/stream-progress.js +195 -0
  441. package/dist/internal/tool-result.d.ts +20 -0
  442. package/dist/internal/tool-result.d.ts.map +1 -0
  443. package/dist/internal/tool-result.js +45 -0
  444. package/dist/internal/trajectory.d.ts +56 -0
  445. package/dist/internal/trajectory.d.ts.map +1 -0
  446. package/dist/internal/trajectory.js +248 -0
  447. package/dist/internal/workspace.d.ts +42 -0
  448. package/dist/internal/workspace.d.ts.map +1 -0
  449. package/dist/internal/workspace.js +199 -0
  450. package/dist/playground/assets/cursor-icons-outline-oY2V_mvK.woff2 +0 -0
  451. package/dist/playground/assets/index-1K-hG-7p.css +1 -0
  452. package/dist/playground/assets/index-FlWjhg3x.js +79 -0
  453. package/dist/playground/index.html +17 -0
  454. package/dist/reminders.d.ts +23 -0
  455. package/dist/reminders.d.ts.map +1 -0
  456. package/dist/reminders.js +22 -0
  457. package/dist/schedules.d.ts +23 -0
  458. package/dist/schedules.d.ts.map +1 -0
  459. package/dist/schedules.js +26 -0
  460. package/dist/skills.d.ts +22 -0
  461. package/dist/skills.d.ts.map +1 -0
  462. package/dist/skills.js +22 -0
  463. package/dist/tools.d.ts +92 -0
  464. package/dist/tools.d.ts.map +1 -0
  465. package/dist/tools.js +68 -0
  466. package/dist/types.d.ts +1609 -0
  467. package/dist/types.d.ts.map +1 -0
  468. package/dist/types.js +80 -0
  469. package/docs/README.md +174 -0
  470. package/docs/ab.md +355 -0
  471. package/docs/building-with-agents.md +129 -0
  472. package/docs/concepts.md +177 -0
  473. package/docs/deployment.md +218 -0
  474. package/docs/evals.md +383 -0
  475. package/docs/guides/agent-to-agent.md +118 -0
  476. package/docs/guides/cloud-runtime.md +122 -0
  477. package/docs/guides/github.md +172 -0
  478. package/docs/guides/human-in-the-loop.md +131 -0
  479. package/docs/guides/slack.md +245 -0
  480. package/docs/guides/webhooks.md +201 -0
  481. package/docs/hillclimbing.md +93 -0
  482. package/docs/quickstart.md +183 -0
  483. package/docs/reference/agent-config.md +154 -0
  484. package/docs/reference/channels.md +206 -0
  485. package/docs/reference/cli.md +257 -0
  486. package/docs/reference/connections.md +137 -0
  487. package/docs/reference/hooks.md +74 -0
  488. package/docs/reference/http-api.md +219 -0
  489. package/docs/reference/instructions.md +77 -0
  490. package/docs/reference/playground.md +67 -0
  491. package/docs/reference/project-layout.md +119 -0
  492. package/docs/reference/schedules.md +220 -0
  493. package/docs/reference/sessions.md +165 -0
  494. package/docs/reference/skills.md +79 -0
  495. package/docs/reference/subagents.md +74 -0
  496. package/docs/reference/tools.md +203 -0
  497. package/docs/scaffolding-agents.md +113 -0
  498. package/docs/troubleshooting.md +95 -0
  499. package/package.json +185 -0
  500. package/skills/ab/SKILL.md +139 -0
  501. package/skills/create-agent/SKILL.md +219 -0
  502. package/skills/debug/SKILL.md +71 -0
  503. package/skills/evals/SKILL.md +217 -0
  504. package/skills/framework-map/SKILL.md +142 -0
  505. package/skills/github/SKILL.md +144 -0
  506. package/skills/hillclimb/SKILL.md +162 -0
  507. package/skills/setup-slack/SKILL.md +180 -0
  508. package/src/ab.ts +675 -0
  509. package/src/bin/agent-serve.ts +949 -0
  510. package/src/channels/github/api.ts +374 -0
  511. package/src/channels/github/auth.ts +275 -0
  512. package/src/channels/github/binding.ts +223 -0
  513. package/src/channels/github/defaults.ts +111 -0
  514. package/src/channels/github/events.ts +72 -0
  515. package/src/channels/github/forward.ts +381 -0
  516. package/src/channels/github/github-channel.ts +2270 -0
  517. package/src/channels/github/index.ts +52 -0
  518. package/src/channels/github/replay.ts +387 -0
  519. package/src/channels/github/state.ts +95 -0
  520. package/src/channels/github/types.ts +490 -0
  521. package/src/channels/slack/api.ts +580 -0
  522. package/src/channels/slack/approvals.ts +198 -0
  523. package/src/channels/slack/assistant.ts +76 -0
  524. package/src/channels/slack/auth.ts +112 -0
  525. package/src/channels/slack/channel-watch.ts +219 -0
  526. package/src/channels/slack/constants.ts +30 -0
  527. package/src/channels/slack/credentials.ts +90 -0
  528. package/src/channels/slack/cursor-account.ts +166 -0
  529. package/src/channels/slack/defaults.ts +450 -0
  530. package/src/channels/slack/dispatch.ts +293 -0
  531. package/src/channels/slack/doctor.ts +135 -0
  532. package/src/channels/slack/event-id-dedupe.ts +30 -0
  533. package/src/channels/slack/inbound.ts +219 -0
  534. package/src/channels/slack/index.ts +45 -0
  535. package/src/channels/slack/init.ts +133 -0
  536. package/src/channels/slack/interactive.ts +194 -0
  537. package/src/channels/slack/links.ts +23 -0
  538. package/src/channels/slack/live-delivery.ts +201 -0
  539. package/src/channels/slack/log.ts +83 -0
  540. package/src/channels/slack/manifest.ts +179 -0
  541. package/src/channels/slack/pr-ref.ts +50 -0
  542. package/src/channels/slack/setup.ts +97 -0
  543. package/src/channels/slack/slack-channel.ts +374 -0
  544. package/src/channels/slack/socket-mode.ts +154 -0
  545. package/src/channels/slack/stream.ts +63 -0
  546. package/src/channels/slack/tool-approval-events.ts +136 -0
  547. package/src/channels/slack/types.ts +349 -0
  548. package/src/channels.ts +385 -0
  549. package/src/connections.ts +89 -0
  550. package/src/evals.ts +501 -0
  551. package/src/hooks.ts +33 -0
  552. package/src/index.ts +184 -0
  553. package/src/internal/ab-collector.ts +210 -0
  554. package/src/internal/ab-fold.ts +228 -0
  555. package/src/internal/ab-snapshot.ts +335 -0
  556. package/src/internal/approval-gate.ts +115 -0
  557. package/src/internal/auth.ts +39 -0
  558. package/src/internal/authored-alias-hooks.ts +69 -0
  559. package/src/internal/authored-loaders.ts +138 -0
  560. package/src/internal/bounded-int.ts +27 -0
  561. package/src/internal/brand.ts +29 -0
  562. package/src/internal/chat-client.ts +407 -0
  563. package/src/internal/cli-ax.ts +474 -0
  564. package/src/internal/cli-cursor.ts +275 -0
  565. package/src/internal/cli-github.ts +849 -0
  566. package/src/internal/cli-slack.ts +175 -0
  567. package/src/internal/cloud-merge.ts +41 -0
  568. package/src/internal/cron.ts +78 -0
  569. package/src/internal/cursor/account-mcp.ts +472 -0
  570. package/src/internal/cursor/backend-client.ts +614 -0
  571. package/src/internal/cursor/credentials.ts +401 -0
  572. package/src/internal/cursor-event-relay.ts +281 -0
  573. package/src/internal/cursor-relay-core.ts +444 -0
  574. package/src/internal/cursor-slack-relay.ts +242 -0
  575. package/src/internal/describe-error.ts +4 -0
  576. package/src/internal/dev-mode.ts +20 -0
  577. package/src/internal/discovery.ts +1709 -0
  578. package/src/internal/distribution.ts +97 -0
  579. package/src/internal/docs-site.ts +191 -0
  580. package/src/internal/duration.ts +39 -0
  581. package/src/internal/eval-run-store.ts +519 -0
  582. package/src/internal/eval-runner.ts +522 -0
  583. package/src/internal/event-log.ts +175 -0
  584. package/src/internal/event-mapper.ts +272 -0
  585. package/src/internal/frontmatter.ts +77 -0
  586. package/src/internal/github-fanout.ts +159 -0
  587. package/src/internal/handleAgentServeTrigger.ts +156 -0
  588. package/src/internal/host-platforms.ts +144 -0
  589. package/src/internal/http-channel.ts +546 -0
  590. package/src/internal/ids.ts +44 -0
  591. package/src/internal/init-project.ts +165 -0
  592. package/src/internal/json-dir-store.ts +109 -0
  593. package/src/internal/json-schema.ts +69 -0
  594. package/src/internal/log-ring.ts +59 -0
  595. package/src/internal/map-pool.ts +32 -0
  596. package/src/internal/mcp-endpoint.ts +518 -0
  597. package/src/internal/mcp-host.ts +211 -0
  598. package/src/internal/net.ts +31 -0
  599. package/src/internal/peer-connections.ts +100 -0
  600. package/src/internal/playground/static.ts +189 -0
  601. package/src/internal/playground/toolchain.ts +277 -0
  602. package/src/internal/reminder-runner.ts +756 -0
  603. package/src/internal/reminder-schedule.ts +106 -0
  604. package/src/internal/reminder-store.ts +145 -0
  605. package/src/internal/resolved-connections.ts +145 -0
  606. package/src/internal/review-comments.ts +268 -0
  607. package/src/internal/router.ts +145 -0
  608. package/src/internal/run-client.ts +320 -0
  609. package/src/internal/schedule-runner.ts +161 -0
  610. package/src/internal/sdk-runner.ts +315 -0
  611. package/src/internal/server.ts +1737 -0
  612. package/src/internal/session-engine.ts +1810 -0
  613. package/src/internal/session-store.ts +147 -0
  614. package/src/internal/session-urls.ts +40 -0
  615. package/src/internal/stream-progress.ts +254 -0
  616. package/src/internal/tool-result.ts +54 -0
  617. package/src/internal/trajectory.ts +306 -0
  618. package/src/internal/workspace.ts +223 -0
  619. package/src/reminders.ts +35 -0
  620. package/src/schedules.ts +37 -0
  621. package/src/skills.ts +27 -0
  622. package/src/tools.ts +127 -0
  623. package/src/types.ts +1848 -0
package/src/ab.ts ADDED
@@ -0,0 +1,675 @@
1
+ /**
2
+ * Live A/B metrics plug-in for agent-serve.
3
+ *
4
+ * Author `agent/ab.ts` (or `agent/ab/<name>.ts`) with {@link defineAB}. At
5
+ * session creation the engine runs {@link ABConfig.split} and appends durable
6
+ * `ab.assigned` events; arms are exposed on {@link SessionInfo.abs} for the
7
+ * agent loop (tools / optional per-variant `instructions`). {@link ABCollector}
8
+ * folds the event stream into metrics and calls {@link ABConfig.onSample}.
9
+ * There is no `agent-serve ab` test runner.
10
+ *
11
+ * Assignments and foldable metrics live in each session's `events.ndjson`
12
+ * (survive serve restart). Optional project defaults in `agent/ab.config.ts`
13
+ * ({@link defineABConfig}) let authors retain the playground fold window and
14
+ * persist samples / aggregate snapshots wherever they like — same idea as
15
+ * eval `persistRuns`.
16
+ *
17
+ * ```ts
18
+ * import { defineAB, splitBySessionHash } from "@anysphere/agent-serve/ab";
19
+ *
20
+ * export default defineAB({
21
+ * name: "concise-instructions",
22
+ * variants: {
23
+ * control: { label: "Baseline" },
24
+ * treatment: { label: "Shorter" },
25
+ * },
26
+ * split: splitBySessionHash({ holdout: 0.1 }),
27
+ * onSample(sample) {
28
+ * console.log(sample.variant, sample.metrics);
29
+ * },
30
+ * });
31
+ * ```
32
+ */
33
+
34
+ import { randomUUID } from "node:crypto";
35
+ import { appendFile, mkdir } from "node:fs/promises";
36
+ import { join } from "node:path";
37
+ import type { ABSnapshot } from "./internal/ab-snapshot.js";
38
+ import { normalizePositiveInt } from "./internal/bounded-int.js";
39
+ import { brandDefinition } from "./internal/brand.js";
40
+ import {
41
+ assertNonEmptyDir,
42
+ deleteFileInDir,
43
+ listJsonBasenames,
44
+ readJsonDir,
45
+ resolveProjectRelativeDir,
46
+ writeJsonFile,
47
+ } from "./internal/json-dir-store.js";
48
+ import type { SessionEvent, SessionInfo } from "./types.js";
49
+
50
+ export type { ABSnapshot } from "./internal/ab-snapshot.js";
51
+
52
+ export interface ABVariantMeta {
53
+ label?: string;
54
+ description?: string;
55
+ /**
56
+ * Optional prompt overlay for this arm. Appended to the agent's
57
+ * instructions for the session (local `AGENTS.md` + cloud first-turn
58
+ * preamble) after sticky enrollment.
59
+ */
60
+ instructions?: string;
61
+ }
62
+
63
+ export interface ABSplitContext {
64
+ agent: { name: string };
65
+ channel: { id: string };
66
+ /**
67
+ * Live sessions only — the engine never calls `split` for
68
+ * `purpose: "eval"` harness sessions.
69
+ */
70
+ session: SessionInfo;
71
+ experiment: string;
72
+ /** Variant keys from {@link ABConfig.variants}, in declaration order. */
73
+ variants: string[];
74
+ }
75
+
76
+ export interface ABCollectContext {
77
+ agent: { name: string };
78
+ channel: { id: string };
79
+ session: SessionInfo;
80
+ experiment: string;
81
+ variant: string;
82
+ }
83
+
84
+ /** Cumulative per-session metrics for one experiment arm. */
85
+ export interface ABMetrics {
86
+ turns: number;
87
+ turnFailures: number;
88
+ toolCalls: number;
89
+ toolErrors: number;
90
+ inputTokens: number;
91
+ outputTokens: number;
92
+ cacheReadTokens: number;
93
+ cacheWriteTokens: number;
94
+ /** Sum of turn wall times (turn.started → turn.completed/failed). */
95
+ wallTimeMs: number;
96
+ /** Custom counters from {@link ABConfig.derive}. */
97
+ custom: Record<string, number>;
98
+ }
99
+
100
+ export type ABSampleReason = "turn.completed" | "turn.failed";
101
+
102
+ export interface ABMetricSample {
103
+ experiment: string;
104
+ variant: string;
105
+ variantLabel?: string;
106
+ sessionId: string;
107
+ channelId: string;
108
+ metrics: ABMetrics;
109
+ reason: ABSampleReason;
110
+ at: string;
111
+ }
112
+
113
+ /** Variant key to enroll, or `null` to leave the session out (sticky). */
114
+ export type ABSplitResult = string | null;
115
+
116
+ /**
117
+ * Sticky enrollment decision. Return a variant key, or `null` to skip.
118
+ * Prefer helpers ({@link splitBySessionHash}, {@link splitIf}, …).
119
+ */
120
+ export type ABSplit = (
121
+ ctx: ABSplitContext
122
+ ) => ABSplitResult | Promise<ABSplitResult>;
123
+
124
+ export interface ABSplitOptions {
125
+ /** Relative weights per variant key (default: equal). */
126
+ weights?: Record<string, number>;
127
+ /** Fraction of sessions left out (`0`–`1`, sticky). */
128
+ holdout?: number;
129
+ /** Salt so a new rollout reshuffles without renaming the experiment. */
130
+ salt?: string;
131
+ }
132
+
133
+ /** Sticky hash of session id → arm (recommended default). */
134
+ export function splitBySessionHash(options?: ABSplitOptions): ABSplit {
135
+ assertHoldout(options?.holdout);
136
+ return (ctx) =>
137
+ pickArm(
138
+ ctx.variants,
139
+ options?.weights,
140
+ hashToUnitInterval(`${options?.salt ?? ""}\0${ctx.session.id}`),
141
+ options?.holdout
142
+ );
143
+ }
144
+
145
+ /** Random draw once per new session (then sticky via persistence). */
146
+ export function splitByRandom(options?: Omit<ABSplitOptions, "salt">): ABSplit {
147
+ assertHoldout(options?.holdout);
148
+ return (ctx) =>
149
+ pickArm(ctx.variants, options?.weights, Math.random(), options?.holdout);
150
+ }
151
+
152
+ /** Pin every new session to one arm. */
153
+ export function splitAlways(variant: string): ABSplit {
154
+ return () => variant;
155
+ }
156
+
157
+ /** Skip every session (pause without deleting the experiment). */
158
+ export function splitNone(): ABSplit {
159
+ return () => null;
160
+ }
161
+
162
+ /** Run `inner` only when `predicate` is true; otherwise skip. */
163
+ export function splitIf(
164
+ predicate: (ctx: ABSplitContext) => boolean | Promise<boolean>,
165
+ inner: ABSplit
166
+ ): ABSplit {
167
+ return async (ctx) => ((await predicate(ctx)) ? inner(ctx) : null);
168
+ }
169
+
170
+ export interface ABConfig {
171
+ /** Experiment id. Defaults to the file stem (`ab/concise.ts` → `concise`). */
172
+ name?: string;
173
+ /**
174
+ * Named arms (at least two). Prefer `control` / `treatment` over `a` / `b`.
175
+ */
176
+ variants: Record<string, ABVariantMeta>;
177
+ /**
178
+ * Choose an arm at session creation, or return `null` to skip. The engine
179
+ * appends a durable `ab.assigned` event; the collector folds that stream.
180
+ */
181
+ split: ABSplit;
182
+ /**
183
+ * Optional custom metric extractors. Return a numeric delta, or
184
+ * `null`/`undefined` to skip. Called on **every** session event (including
185
+ * high-frequency `message.appended` deltas) — keep these synchronous and
186
+ * cheap; do not await I/O.
187
+ */
188
+ derive?: Record<
189
+ string,
190
+ (
191
+ event: SessionEvent,
192
+ ctx: ABCollectContext
193
+ ) => number | null | undefined | Promise<number | null | undefined>
194
+ >;
195
+ /**
196
+ * Called on turn complete/fail for enrolled sessions only. Observe-only;
197
+ * errors are logged and never fatal.
198
+ */
199
+ onSample: (sample: ABMetricSample) => void | Promise<void>;
200
+ }
201
+
202
+ export type ABDefinition = ABConfig & { readonly __agentServe: "ab" };
203
+
204
+ /** Author a live A/B metrics experiment under `agent/ab`. */
205
+ export function defineAB(config: ABConfig): ABDefinition {
206
+ const keys = Object.keys(config.variants ?? {});
207
+ if (keys.length < 2) {
208
+ throw new Error(
209
+ `defineAB: variants needs at least two arms (got ${keys.length})`
210
+ );
211
+ }
212
+ for (const key of keys) {
213
+ if (key.length === 0 || key.includes("/") || key.includes("\\")) {
214
+ throw new Error(
215
+ `defineAB: invalid variant key ${JSON.stringify(key)} (single segment)`
216
+ );
217
+ }
218
+ }
219
+ if (typeof config.split !== "function") {
220
+ throw new Error(
221
+ "defineAB: split must be a function (splitBySessionHash(), splitIf(...), or custom)"
222
+ );
223
+ }
224
+ if (typeof config.onSample !== "function") {
225
+ throw new Error("defineAB: onSample(sample) is required");
226
+ }
227
+ if (config.derive !== undefined) {
228
+ for (const [key, fn] of Object.entries(config.derive)) {
229
+ if (typeof fn !== "function") {
230
+ throw new Error(`defineAB: derive.${key} must be a function`);
231
+ }
232
+ }
233
+ }
234
+ return brandDefinition("ab", config);
235
+ }
236
+
237
+ /** Resolve {@link ABConfig.split} to a variant key or `null`. */
238
+ export async function resolveABSplit(
239
+ split: ABSplit,
240
+ ctx: ABSplitContext
241
+ ): Promise<ABSplitResult> {
242
+ const result = await split(ctx);
243
+ if (result === null || result === undefined) {
244
+ return null;
245
+ }
246
+ if (typeof result === "string") {
247
+ return result;
248
+ }
249
+ throw new Error(
250
+ `defineAB: split must return a variant key or null (got ${typeof result})`
251
+ );
252
+ }
253
+
254
+ function assertHoldout(holdout: number | undefined): void {
255
+ if (holdout === undefined) {
256
+ return;
257
+ }
258
+ if (!Number.isFinite(holdout) || holdout < 0 || holdout > 1) {
259
+ throw new Error("defineAB: holdout must be a finite number in [0, 1]");
260
+ }
261
+ }
262
+
263
+ /** Pick a variant (or `null` via holdout) from a unit interval in `[0, 1)`. */
264
+ function pickArm(
265
+ variantKeys: readonly string[],
266
+ weights: Record<string, number> | undefined,
267
+ unitInterval: number,
268
+ holdout: number | undefined
269
+ ): ABSplitResult {
270
+ const u = clampUnit(unitInterval);
271
+ const out = holdout ?? 0;
272
+ if (out >= 1 || (out > 0 && u < out)) {
273
+ return null;
274
+ }
275
+ const remapped = out > 0 ? (u - out) / (1 - out) : u;
276
+ return pickWeightedVariant(variantKeys, weights, remapped);
277
+ }
278
+
279
+ /** FNV-1a → [0, 1). */
280
+ export function hashToUnitInterval(input: string): number {
281
+ let h = 2166136261;
282
+ for (let i = 0; i < input.length; i++) {
283
+ h ^= input.charCodeAt(i);
284
+ h = Math.imul(h, 16777619);
285
+ }
286
+ return (h >>> 0) / 0x1_0000_0000;
287
+ }
288
+
289
+ export function pickWeightedVariant(
290
+ variantKeys: readonly string[],
291
+ weights: Record<string, number> | undefined,
292
+ unitInterval: number
293
+ ): string {
294
+ const u = clampUnit(unitInterval);
295
+ const rows: Array<{ key: string; weight: number }> = [];
296
+ let total = 0;
297
+ for (const key of variantKeys) {
298
+ const weight = weights?.[key] ?? 1;
299
+ if (weight > 0 && Number.isFinite(weight)) {
300
+ rows.push({ key, weight });
301
+ total += weight;
302
+ }
303
+ }
304
+ if (rows.length === 0 || total <= 0) {
305
+ throw new Error("defineAB: no positive-weight variants to split across");
306
+ }
307
+ let cursor = u * total;
308
+ for (const row of rows) {
309
+ cursor -= row.weight;
310
+ if (cursor < 0) {
311
+ return row.key;
312
+ }
313
+ }
314
+ return rows[rows.length - 1]!.key;
315
+ }
316
+
317
+ function clampUnit(unitInterval: number): number {
318
+ return Math.min(Math.max(unitInterval, 0), 0.999999999);
319
+ }
320
+
321
+ export function emptyABMetrics(): ABMetrics {
322
+ return {
323
+ turns: 0,
324
+ turnFailures: 0,
325
+ toolCalls: 0,
326
+ toolErrors: 0,
327
+ inputTokens: 0,
328
+ outputTokens: 0,
329
+ cacheReadTokens: 0,
330
+ cacheWriteTokens: 0,
331
+ wallTimeMs: 0,
332
+ custom: {},
333
+ };
334
+ }
335
+
336
+ export function cloneABMetrics(metrics: ABMetrics): ABMetrics {
337
+ return { ...metrics, custom: { ...metrics.custom } };
338
+ }
339
+
340
+ /**
341
+ * Append enrolled per-variant `instructions` overlays onto the base agent
342
+ * prompt. Used by the session engine before materializing the harness.
343
+ */
344
+ export function mergeABInstructions(
345
+ base: string | undefined,
346
+ abs: Readonly<Record<string, string | null>> | undefined,
347
+ experiments: ReadonlyArray<{ name: string; definition: ABDefinition }>
348
+ ): string | undefined {
349
+ if (abs === undefined) {
350
+ return base;
351
+ }
352
+ const extras: string[] = [];
353
+ for (const experiment of experiments) {
354
+ const variant = abs[experiment.name];
355
+ if (variant === undefined || variant === null) {
356
+ continue;
357
+ }
358
+ const extra = experiment.definition.variants[variant]?.instructions;
359
+ if (typeof extra === "string" && extra.trim() !== "") {
360
+ extras.push(extra.trim());
361
+ }
362
+ }
363
+ if (extras.length === 0) {
364
+ return base;
365
+ }
366
+ const baseText = base?.trim() ?? "";
367
+ return baseText === ""
368
+ ? extras.join("\n\n")
369
+ : `${baseText}\n\n${extras.join("\n\n")}`;
370
+ }
371
+
372
+ // ---------------------------------------------------------------------------
373
+ // Project config (`agent/ab.config.ts`) — fold window + optional persistence
374
+ // ---------------------------------------------------------------------------
375
+
376
+ /** Default sessions folded into `GET /v1/abs` / the A/Bs tab. */
377
+ export const AB_DEFAULT_MAX_PLAYGROUND_SESSIONS = 200;
378
+
379
+ /** Hard ceiling for {@link ABConfigFile.maxPlaygroundSessions}. */
380
+ export const AB_MAX_PLAYGROUND_SESSIONS = 10_000;
381
+
382
+ /** Default snapshot files kept when {@link ABConfigFile.persistSnapshots} is set. */
383
+ export const AB_DEFAULT_RETAIN_SNAPSHOTS = 20;
384
+
385
+ /** Hard ceiling for {@link ABConfigFile.retainSnapshots}. */
386
+ export const AB_MAX_RETAIN_SNAPSHOTS = 500;
387
+
388
+ /** Context passed to A/B persistence hooks. */
389
+ export interface ABPersistContext {
390
+ /** Absolute agent project root (directory that contains `agent/`). */
391
+ projectRoot: string;
392
+ }
393
+
394
+ /**
395
+ * Durable sink for {@link ABMetricSample} rows (same payload as
396
+ * {@link ABConfig.onSample}). Session `events.ndjson` remains the source of
397
+ * truth for assignment + fold; this is for analytics export.
398
+ */
399
+ export interface ABSamplePersistence {
400
+ save(sample: ABMetricSample, ctx: ABPersistContext): void | Promise<void>;
401
+ }
402
+
403
+ /**
404
+ * Durable store for playground-style aggregate snapshots (`GET /v1/abs`).
405
+ * Without this, aggregates are always recomputed from session logs (nothing
406
+ * is kept after sessions are pruned).
407
+ *
408
+ * **Runtime note:** `serve` currently only calls {@link save} (after each
409
+ * `GET /v1/abs`). {@link load} / {@link delete} are for author tooling or a
410
+ * custom store; they are not wired into the playground hydrate path.
411
+ */
412
+ export interface ABSnapshotPersistence {
413
+ load(ctx: ABPersistContext): ABSnapshot[] | Promise<ABSnapshot[]>;
414
+ save(snapshot: ABSnapshot, ctx: ABPersistContext): void | Promise<void>;
415
+ delete?(snapshotId: string, ctx: ABPersistContext): void | Promise<void>;
416
+ }
417
+
418
+ /** Lets {@link defineABConfig} push `retainSnapshots` into {@link persistABSnapshotsToDir}. */
419
+ const abSnapshotRetainState = new WeakMap<
420
+ ABSnapshotPersistence,
421
+ { retain: number }
422
+ >();
423
+
424
+ /**
425
+ * Project-wide A/B defaults (`agent/ab.config.ts`).
426
+ *
427
+ * **Assignments and foldable metrics** already persist in each session's
428
+ * `events.ndjson` under `--state-root`. Use this config to:
429
+ * - cap how many sessions the playground / `GET /v1/abs` folds
430
+ * - export samples and/or aggregate snapshots to author-chosen storage
431
+ */
432
+ export interface ABConfigFile {
433
+ /**
434
+ * Optional. Max newest sessions folded into **`GET /v1/abs` / the A/Bs
435
+ * tab** (and into {@link persistSnapshots} payloads). Does not prune
436
+ * session logs, change assignment, or limit {@link persistSamples}.
437
+ *
438
+ * Default {@link AB_DEFAULT_MAX_PLAYGROUND_SESSIONS}; hard-capped at
439
+ * {@link AB_MAX_PLAYGROUND_SESSIONS}.
440
+ */
441
+ maxPlaygroundSessions?: number;
442
+ /**
443
+ * Snapshot-file budget when {@link persistSnapshots} is set.
444
+ * Default {@link AB_DEFAULT_RETAIN_SNAPSHOTS}; hard-capped at
445
+ * {@link AB_MAX_RETAIN_SNAPSHOTS}.
446
+ *
447
+ * When using {@link persistABSnapshotsToDir}, this value controls on-disk
448
+ * pruning (overriding the helper's `{ retain }` default). Custom stores
449
+ * should apply the same budget themselves.
450
+ */
451
+ retainSnapshots?: number;
452
+ /**
453
+ * Optional sink for cumulative metric samples (in addition to each
454
+ * experiment's {@link ABConfig.onSample}).
455
+ *
456
+ * ```ts
457
+ * persistSamples: persistABSamplesToDir(".agent-serve/ab-samples"),
458
+ * ```
459
+ */
460
+ persistSamples?: ABSamplePersistence;
461
+ /**
462
+ * Optional store for aggregate snapshots written when `GET /v1/abs` runs.
463
+ *
464
+ * ```ts
465
+ * persistSnapshots: persistABSnapshotsToDir(".agent-serve/ab-snapshots"),
466
+ * ```
467
+ */
468
+ persistSnapshots?: ABSnapshotPersistence;
469
+ }
470
+
471
+ /**
472
+ * Defaults for every experiment under `agent/ab*` (`agent/ab.config.ts`).
473
+ *
474
+ * Session event logs remain the assignment/metrics source of truth; this
475
+ * file controls the playground fold window and optional durable exports.
476
+ */
477
+ export function defineABConfig(config: ABConfigFile): ABConfigFile {
478
+ const persistSamples = normalizeSamplePersistence(config.persistSamples);
479
+ const persistSnapshots = normalizeSnapshotPersistence(
480
+ config.persistSnapshots
481
+ );
482
+ const helperRetain =
483
+ persistSnapshots !== undefined
484
+ ? abSnapshotRetainState.get(persistSnapshots)
485
+ : undefined;
486
+ let retainSnapshots: number | undefined;
487
+ if (config.retainSnapshots !== undefined) {
488
+ retainSnapshots = normalizeABRetainSnapshots(config.retainSnapshots);
489
+ if (helperRetain !== undefined) {
490
+ helperRetain.retain = retainSnapshots;
491
+ }
492
+ } else if (persistSnapshots !== undefined) {
493
+ retainSnapshots =
494
+ helperRetain?.retain ??
495
+ normalizeABRetainSnapshots(AB_DEFAULT_RETAIN_SNAPSHOTS);
496
+ }
497
+ return {
498
+ maxPlaygroundSessions: normalizeABMaxPlaygroundSessions(
499
+ config.maxPlaygroundSessions ?? AB_DEFAULT_MAX_PLAYGROUND_SESSIONS
500
+ ),
501
+ ...(retainSnapshots === undefined ? {} : { retainSnapshots }),
502
+ ...(persistSamples === undefined ? {} : { persistSamples }),
503
+ ...(persistSnapshots === undefined ? {} : { persistSnapshots }),
504
+ };
505
+ }
506
+
507
+ /** Validate {@link ABConfigFile.maxPlaygroundSessions}. */
508
+ export function normalizeABMaxPlaygroundSessions(value: unknown): number {
509
+ return normalizePositiveInt(value, {
510
+ label: "ab.config.ts: maxPlaygroundSessions",
511
+ max: AB_MAX_PLAYGROUND_SESSIONS,
512
+ });
513
+ }
514
+
515
+ /**
516
+ * Effective `GET /v1/abs` fold window. Invalid values fall back to
517
+ * {@link AB_DEFAULT_MAX_PLAYGROUND_SESSIONS}.
518
+ */
519
+ export function resolveABMaxPlaygroundSessions(value: unknown): number {
520
+ if (value === undefined) {
521
+ return AB_DEFAULT_MAX_PLAYGROUND_SESSIONS;
522
+ }
523
+ try {
524
+ return normalizeABMaxPlaygroundSessions(value);
525
+ } catch {
526
+ return AB_DEFAULT_MAX_PLAYGROUND_SESSIONS;
527
+ }
528
+ }
529
+
530
+ /** Validate {@link ABConfigFile.retainSnapshots}. */
531
+ export function normalizeABRetainSnapshots(value: unknown): number {
532
+ return normalizePositiveInt(value, {
533
+ label: "ab.config.ts: retainSnapshots",
534
+ max: AB_MAX_RETAIN_SNAPSHOTS,
535
+ });
536
+ }
537
+
538
+ function normalizeSamplePersistence(
539
+ value: ABSamplePersistence | undefined
540
+ ): ABSamplePersistence | undefined {
541
+ if (value === undefined) {
542
+ return undefined;
543
+ }
544
+ if (
545
+ typeof value !== "object" ||
546
+ value === null ||
547
+ typeof value.save !== "function"
548
+ ) {
549
+ throw new Error(
550
+ "ab.config.ts: persistSamples must define save(sample, ctx)"
551
+ );
552
+ }
553
+ return value;
554
+ }
555
+
556
+ function normalizeSnapshotPersistence(
557
+ value: ABSnapshotPersistence | undefined
558
+ ): ABSnapshotPersistence | undefined {
559
+ if (value === undefined) {
560
+ return undefined;
561
+ }
562
+ if (
563
+ typeof value !== "object" ||
564
+ value === null ||
565
+ typeof value.load !== "function" ||
566
+ typeof value.save !== "function"
567
+ ) {
568
+ throw new Error(
569
+ "ab.config.ts: persistSnapshots must define load(ctx) and save(snapshot, ctx)"
570
+ );
571
+ }
572
+ if (value.delete !== undefined && typeof value.delete !== "function") {
573
+ throw new Error("ab.config.ts: persistSnapshots.delete must be a function");
574
+ }
575
+ return value;
576
+ }
577
+
578
+ /**
579
+ * Append-only NDJSON {@link ABSamplePersistence} under `dir` (relative to
580
+ * the agent project root).
581
+ *
582
+ * ```ts
583
+ * // agent/ab.config.ts
584
+ * export default defineABConfig({
585
+ * persistSamples: persistABSamplesToDir(".agent-serve/ab-samples"),
586
+ * });
587
+ * ```
588
+ */
589
+ export function persistABSamplesToDir(dir: string): ABSamplePersistence {
590
+ const relativeDir = assertNonEmptyDir(dir, "persistABSamplesToDir");
591
+ const resolveDir = (projectRoot: string): string =>
592
+ resolveProjectRelativeDir({ dir: relativeDir, projectRoot });
593
+
594
+ return {
595
+ async save(sample, ctx) {
596
+ const root = resolveDir(ctx.projectRoot);
597
+ await mkdir(root, { recursive: true });
598
+ await appendFile(
599
+ join(root, "samples.ndjson"),
600
+ `${JSON.stringify(sample)}\n`,
601
+ "utf8"
602
+ );
603
+ },
604
+ };
605
+ }
606
+
607
+ /**
608
+ * Filesystem {@link ABSnapshotPersistence}: one JSON file per snapshot under
609
+ * `dir`, pruned to `retain` files (default {@link AB_DEFAULT_RETAIN_SNAPSHOTS}).
610
+ *
611
+ * Prefer setting {@link ABConfigFile.retainSnapshots} in `defineABConfig` —
612
+ * that value is applied to this helper's prune budget. Pass `{ retain }` here
613
+ * only when constructing the helper outside `defineABConfig`, or as a default
614
+ * that `retainSnapshots` may override.
615
+ */
616
+ export function persistABSnapshotsToDir(
617
+ dir: string,
618
+ options?: { retain?: number }
619
+ ): ABSnapshotPersistence {
620
+ const relativeDir = assertNonEmptyDir(dir, "persistABSnapshotsToDir");
621
+ const retainState = {
622
+ retain: normalizeABRetainSnapshots(
623
+ options?.retain ?? AB_DEFAULT_RETAIN_SNAPSHOTS
624
+ ),
625
+ };
626
+ const resolveDir = (projectRoot: string): string =>
627
+ resolveProjectRelativeDir({ dir: relativeDir, projectRoot });
628
+ let saveChain: Promise<void> = Promise.resolve();
629
+
630
+ const persist: ABSnapshotPersistence = {
631
+ async load(ctx) {
632
+ return readJsonDir<ABSnapshot>(resolveDir(ctx.projectRoot), {
633
+ sortBasenames: "desc",
634
+ });
635
+ },
636
+ async save(snapshot, ctx) {
637
+ const run = async (): Promise<void> => {
638
+ const root = resolveDir(ctx.projectRoot);
639
+ const snapshotId =
640
+ snapshot.config?.snapshotId ??
641
+ `absnap_${randomUUID().replace(/-/g, "")}`;
642
+ const toWrite: ABSnapshot = {
643
+ ...snapshot,
644
+ config: {
645
+ ...snapshot.config,
646
+ snapshotId,
647
+ },
648
+ };
649
+ await writeJsonFile({
650
+ root,
651
+ fileName: `${snapshotId}.json`,
652
+ value: toWrite,
653
+ });
654
+ const names = (await listJsonBasenames(root)).sort().reverse();
655
+ for (const stale of names.slice(retainState.retain)) {
656
+ await deleteFileInDir({ root, fileName: stale });
657
+ }
658
+ };
659
+ const next = saveChain.then(run, run);
660
+ saveChain = next.then(
661
+ () => undefined,
662
+ () => undefined
663
+ );
664
+ await next;
665
+ },
666
+ async delete(snapshotId, ctx) {
667
+ await deleteFileInDir({
668
+ root: resolveDir(ctx.projectRoot),
669
+ fileName: `${snapshotId}.json`,
670
+ });
671
+ },
672
+ };
673
+ abSnapshotRetainState.set(persist, retainState);
674
+ return persist;
675
+ }