@cursor/july 0.1.26 → 0.1.28

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 (324) hide show
  1. package/dist/ab.d.ts +6 -0
  2. package/dist/ab.d.ts.map +1 -1
  3. package/dist/ab.js +1 -0
  4. package/dist/artifacts.d.ts +34 -0
  5. package/dist/artifacts.d.ts.map +1 -0
  6. package/dist/artifacts.js +57 -0
  7. package/dist/bin/agent-serve.js +43 -12
  8. package/dist/channels/github/github-channel.d.ts +1 -1
  9. package/dist/channels/github/github-channel.d.ts.map +1 -1
  10. package/dist/channels/github/github-channel.js +14 -1
  11. package/dist/channels/github/types.d.ts +6 -1
  12. package/dist/channels/github/types.d.ts.map +1 -1
  13. package/dist/channels/slack/api.d.ts +2 -1
  14. package/dist/channels/slack/api.d.ts.map +1 -1
  15. package/dist/channels/slack/api.js +1 -0
  16. package/dist/channels/slack/auth.d.ts +13 -13
  17. package/dist/channels/slack/auth.d.ts.map +1 -1
  18. package/dist/channels/slack/auth.js +19 -10
  19. package/dist/channels/slack/cursor-account.d.ts +2 -1
  20. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  21. package/dist/channels/slack/cursor-account.js +3 -1
  22. package/dist/channels/slack/defaults.d.ts +7 -4
  23. package/dist/channels/slack/defaults.d.ts.map +1 -1
  24. package/dist/channels/slack/defaults.js +14 -4
  25. package/dist/channels/slack/dispatch.d.ts +10 -1
  26. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  27. package/dist/channels/slack/dispatch.js +59 -21
  28. package/dist/channels/slack/external-policy.d.ts +27 -0
  29. package/dist/channels/slack/external-policy.d.ts.map +1 -0
  30. package/dist/channels/slack/external-policy.js +53 -0
  31. package/dist/channels/slack/index.d.ts +1 -0
  32. package/dist/channels/slack/index.d.ts.map +1 -1
  33. package/dist/channels/slack/index.js +1 -0
  34. package/dist/channels/slack/interactive.d.ts +8 -1
  35. package/dist/channels/slack/interactive.d.ts.map +1 -1
  36. package/dist/channels/slack/interactive.js +43 -15
  37. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  38. package/dist/channels/slack/slack-channel.js +22 -8
  39. package/dist/channels/slack/types.d.ts +16 -1
  40. package/dist/channels/slack/types.d.ts.map +1 -1
  41. package/dist/docs/404.html +2 -2
  42. package/dist/docs/ab.html +3 -3
  43. package/dist/docs/assets/{app.DYcC9FY-.js → app.CqjrYaT2.js} +1 -1
  44. package/dist/docs/assets/chunks/@localSearchIndexroot.VWQoPFJ1.js +1 -0
  45. package/dist/docs/assets/chunks/{VPLocalSearchBox.o4N_knTV.js → VPLocalSearchBox.ag-MwUel.js} +1 -1
  46. package/dist/docs/assets/chunks/{theme.DQ-njyo0.js → theme.BS-DJWat.js} +2 -2
  47. package/dist/docs/assets/{guides_slack.md.D_xFKlnF.js → guides_slack.md.CjmJSvZS.js} +8 -4
  48. package/dist/docs/assets/guides_slack.md.CjmJSvZS.lean.js +1 -0
  49. package/dist/docs/assets/guides_webhooks.md.wSOYas3X.js +183 -0
  50. package/dist/docs/assets/{guides_webhooks.md.B1EswtUu.lean.js → guides_webhooks.md.wSOYas3X.lean.js} +1 -1
  51. package/dist/docs/assets/{index.md.Dfv5ic9t.js → index.md.CH_s5uZe.js} +1 -1
  52. package/dist/docs/assets/{reference_channels.md.DdmiKgqf.js → reference_channels.md.CDhTRfUz.js} +2 -2
  53. package/dist/docs/assets/{reference_channels.md.DdmiKgqf.lean.js → reference_channels.md.CDhTRfUz.lean.js} +1 -1
  54. package/dist/docs/building-with-agents.html +3 -3
  55. package/dist/docs/concepts.html +3 -3
  56. package/dist/docs/deployment.html +3 -3
  57. package/dist/docs/evals.html +3 -3
  58. package/dist/docs/example-agents/approval-buddy.html +3 -3
  59. package/dist/docs/example-agents/benny.html +3 -3
  60. package/dist/docs/example-agents/bugbot.html +3 -3
  61. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  62. package/dist/docs/example-agents/codeowners-review.html +3 -3
  63. package/dist/docs/example-agents/concierge.html +3 -3
  64. package/dist/docs/example-agents/fsd.html +3 -3
  65. package/dist/docs/example-agents/index.html +3 -3
  66. package/dist/docs/example-agents/knowledge-base.html +3 -3
  67. package/dist/docs/example-agents/oncall.html +3 -3
  68. package/dist/docs/example-agents/security-reviewer.html +3 -3
  69. package/dist/docs/example-agents/slack-agent.html +3 -3
  70. package/dist/docs/example-agents/weather-agent.html +3 -3
  71. package/dist/docs/guides/agent-to-agent.html +3 -3
  72. package/dist/docs/guides/cloud-runtime.html +3 -3
  73. package/dist/docs/guides/github.html +3 -3
  74. package/dist/docs/guides/human-in-the-loop.html +3 -3
  75. package/dist/docs/guides/mcp-oauth.html +3 -3
  76. package/dist/docs/guides/slack.html +11 -7
  77. package/dist/docs/guides/webhooks.html +138 -5
  78. package/dist/docs/hashmap.json +1 -1
  79. package/dist/docs/hillclimbing.html +3 -3
  80. package/dist/docs/index.html +5 -5
  81. package/dist/docs/quickstart.html +3 -3
  82. package/dist/docs/reference/agent-config.html +3 -3
  83. package/dist/docs/reference/channels.html +5 -5
  84. package/dist/docs/reference/cli.html +3 -3
  85. package/dist/docs/reference/connections.html +3 -3
  86. package/dist/docs/reference/hooks.html +3 -3
  87. package/dist/docs/reference/http-api.html +3 -3
  88. package/dist/docs/reference/instructions.html +3 -3
  89. package/dist/docs/reference/playground.html +3 -3
  90. package/dist/docs/reference/project-layout.html +3 -3
  91. package/dist/docs/reference/prompt.html +3 -3
  92. package/dist/docs/reference/schedules.html +3 -3
  93. package/dist/docs/reference/sessions.html +3 -3
  94. package/dist/docs/reference/skills.html +3 -3
  95. package/dist/docs/reference/subagents.html +3 -3
  96. package/dist/docs/reference/tools.html +3 -3
  97. package/dist/docs/scaffolding-agents.html +3 -3
  98. package/dist/docs/storage.html +3 -3
  99. package/dist/docs/troubleshooting.html +3 -3
  100. package/dist/evals/assertions.d.ts +10 -1
  101. package/dist/evals/assertions.d.ts.map +1 -1
  102. package/dist/evals/assertions.js +12 -0
  103. package/dist/evals.d.ts +1 -1
  104. package/dist/evals.d.ts.map +1 -1
  105. package/dist/index.d.ts +1 -0
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +1 -0
  108. package/dist/internal/ab-fold.d.ts.map +1 -1
  109. package/dist/internal/ab-fold.js +4 -0
  110. package/dist/internal/ab-snapshot.d.ts.map +1 -1
  111. package/dist/internal/ab-snapshot.js +1 -0
  112. package/dist/internal/artifacts-store.d.ts +45 -0
  113. package/dist/internal/artifacts-store.d.ts.map +1 -0
  114. package/dist/internal/artifacts-store.js +250 -0
  115. package/dist/internal/builtin-tools/artifacts.d.ts +10 -0
  116. package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -0
  117. package/dist/internal/builtin-tools/artifacts.js +73 -0
  118. package/dist/internal/builtin-tools/index.d.ts +8 -1
  119. package/dist/internal/builtin-tools/index.d.ts.map +1 -1
  120. package/dist/internal/builtin-tools/index.js +15 -0
  121. package/dist/internal/cli-ax.d.ts +19 -0
  122. package/dist/internal/cli-ax.d.ts.map +1 -1
  123. package/dist/internal/cli-ax.js +196 -57
  124. package/dist/internal/cli-slack.d.ts.map +1 -1
  125. package/dist/internal/cli-slack.js +14 -6
  126. package/dist/internal/cursor-slack-relay.d.ts +2 -0
  127. package/dist/internal/cursor-slack-relay.d.ts.map +1 -1
  128. package/dist/internal/cursor-slack-relay.js +1 -0
  129. package/dist/internal/deploy-client.d.ts +2 -0
  130. package/dist/internal/deploy-client.d.ts.map +1 -1
  131. package/dist/internal/deploy-client.js +1 -0
  132. package/dist/internal/discovery.d.ts.map +1 -1
  133. package/dist/internal/discovery.js +91 -3
  134. package/dist/internal/distribution.d.ts.map +1 -1
  135. package/dist/internal/distribution.js +1 -0
  136. package/dist/internal/model-pricing.d.ts +49 -0
  137. package/dist/internal/model-pricing.d.ts.map +1 -0
  138. package/dist/internal/model-pricing.js +377 -0
  139. package/dist/internal/reminder-runner.d.ts.map +1 -1
  140. package/dist/internal/reminder-runner.js +1 -0
  141. package/dist/internal/schedule-runner.d.ts.map +1 -1
  142. package/dist/internal/schedule-runner.js +1 -0
  143. package/dist/internal/sdk-runner.d.ts.map +1 -1
  144. package/dist/internal/sdk-runner.js +9 -1
  145. package/dist/internal/server.d.ts.map +1 -1
  146. package/dist/internal/server.js +49 -5
  147. package/dist/internal/session-cost.d.ts +49 -0
  148. package/dist/internal/session-cost.d.ts.map +1 -0
  149. package/dist/internal/session-cost.js +175 -0
  150. package/dist/internal/session-engine.d.ts +35 -1
  151. package/dist/internal/session-engine.d.ts.map +1 -1
  152. package/dist/internal/session-engine.js +128 -7
  153. package/dist/internal/storage-coordinator.d.ts +15 -0
  154. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  155. package/dist/internal/storage-coordinator.js +52 -0
  156. package/dist/internal/trajectory.d.ts +9 -1
  157. package/dist/internal/trajectory.d.ts.map +1 -1
  158. package/dist/internal/trajectory.js +32 -5
  159. package/dist/playground/assets/index-Bul1WfrB.css +1 -0
  160. package/dist/playground/assets/index-CkN74U_L.js +87 -0
  161. package/dist/playground/index.html +2 -2
  162. package/dist/storage.d.ts +4 -0
  163. package/dist/storage.d.ts.map +1 -1
  164. package/dist/storage.js +4 -0
  165. package/dist/types.d.ts +164 -1
  166. package/dist/types.d.ts.map +1 -1
  167. package/docs/README.md +1 -0
  168. package/docs/guides/slack.md +32 -7
  169. package/docs/guides/webhooks.md +183 -0
  170. package/docs/reference/channels.md +2 -1
  171. package/package.json +26 -26
  172. package/skills/setup-slack/SKILL.md +3 -0
  173. package/src/ab.ts +7 -0
  174. package/src/bin/agent-serve.ts +42 -5
  175. package/src/channels/slack/auth.ts +29 -22
  176. package/src/channels/slack/cursor-account.ts +2 -0
  177. package/src/channels/slack/defaults.ts +24 -4
  178. package/src/channels/slack/dispatch.ts +42 -1
  179. package/src/channels/slack/external-policy.ts +62 -0
  180. package/src/channels/slack/index.ts +1 -0
  181. package/src/channels/slack/interactive.ts +42 -10
  182. package/src/channels/slack/slack-channel.ts +20 -3
  183. package/src/channels/slack/types.ts +12 -0
  184. package/src/internal/ab-fold.ts +4 -0
  185. package/src/internal/ab-snapshot.ts +1 -0
  186. package/src/internal/cli-ax.ts +205 -34
  187. package/src/internal/cursor-slack-relay.ts +3 -0
  188. package/src/internal/deploy-client.ts +3 -0
  189. package/src/internal/discovery.ts +8 -1
  190. package/src/internal/model-pricing.ts +426 -0
  191. package/src/internal/sdk-runner.ts +10 -2
  192. package/src/internal/server.ts +9 -5
  193. package/src/internal/session-cost.ts +231 -0
  194. package/src/internal/session-engine.ts +28 -0
  195. package/src/internal/trajectory.ts +45 -3
  196. package/src/types.ts +24 -1
  197. package/dist/channels/github/instrument.d.ts +0 -20
  198. package/dist/channels/github/instrument.d.ts.map +0 -1
  199. package/dist/docs/assets/chunks/@localSearchIndexroot.BQTzJjR_.js +0 -1
  200. package/dist/docs/assets/guides_slack.md.D_xFKlnF.lean.js +0 -1
  201. package/dist/docs/assets/guides_webhooks.md.B1EswtUu.js +0 -50
  202. package/dist/internal/json-dir-store.d.ts +0 -32
  203. package/dist/internal/json-dir-store.d.ts.map +0 -1
  204. package/dist/internal/json-dir-store.js +0 -100
  205. package/dist/playground/assets/index-CjOQ4hN9.css +0 -1
  206. package/dist/playground/assets/index-DqXdAFGa.js +0 -85
  207. package/src/bin/agent-serve.version.test.ts +0 -64
  208. package/src/channels/github/api.test.ts +0 -64
  209. package/src/channels/github/auth.test.ts +0 -105
  210. package/src/channels/github/cursor-account.test.ts +0 -204
  211. package/src/channels/github/forward.test.ts +0 -457
  212. package/src/channels/github/github.test.ts +0 -985
  213. package/src/channels/github/replay.test.ts +0 -179
  214. package/src/channels/slack/api.post-message.test.ts +0 -148
  215. package/src/channels/slack/approvals.test.ts +0 -328
  216. package/src/channels/slack/block-actions.test.ts +0 -469
  217. package/src/channels/slack/bot-mentions.test.ts +0 -267
  218. package/src/channels/slack/channel-watch.test.ts +0 -363
  219. package/src/channels/slack/cursor-account.test.ts +0 -301
  220. package/src/channels/slack/defaults.final-post.test.ts +0 -182
  221. package/src/channels/slack/dispatch.test.ts +0 -875
  222. package/src/channels/slack/eval-directive.test.ts +0 -273
  223. package/src/channels/slack/message-body.test.ts +0 -54
  224. package/src/channels/slack/nudge-store.test.ts +0 -143
  225. package/src/channels/slack/slack.test.ts +0 -391
  226. package/src/channels/slack/stop.test.ts +0 -23
  227. package/src/channels/slack/thread-context.test.ts +0 -202
  228. package/src/evals/assertions.test.ts +0 -617
  229. package/src/evals/expect.test.ts +0 -144
  230. package/src/evals/judge.test.ts +0 -181
  231. package/src/evals/loaders.test.ts +0 -132
  232. package/src/evals/matchers.test.ts +0 -95
  233. package/src/evals/reporters.test.ts +0 -303
  234. package/src/evals/run-facts.test.ts +0 -259
  235. package/src/internal/ab-snapshot.test.ts +0 -325
  236. package/src/internal/approval-gate.test.ts +0 -49
  237. package/src/internal/approvals.integration.test.ts +0 -383
  238. package/src/internal/artifacts-store.test.ts +0 -283
  239. package/src/internal/authored-loaders.test.ts +0 -31
  240. package/src/internal/builtin-tools/artifacts.test.ts +0 -247
  241. package/src/internal/builtin-tools/reminders.test.ts +0 -201
  242. package/src/internal/channel-route-schema.test.ts +0 -294
  243. package/src/internal/chat-attach.test.ts +0 -262
  244. package/src/internal/cli-deploy.test.ts +0 -1991
  245. package/src/internal/cli-docs.test.ts +0 -161
  246. package/src/internal/cli-mcp.test.ts +0 -789
  247. package/src/internal/cli-skills.test.ts +0 -133
  248. package/src/internal/cli-slack.test.ts +0 -1654
  249. package/src/internal/cloud-merge.test.ts +0 -74
  250. package/src/internal/cron.test.ts +0 -22
  251. package/src/internal/cursor/account-mcp.test.ts +0 -807
  252. package/src/internal/cursor/backend-client.test.ts +0 -591
  253. package/src/internal/cursor/credentials.test.ts +0 -351
  254. package/src/internal/cursor/github-credentials.test.ts +0 -136
  255. package/src/internal/cursor-account-mcp-auth.test.ts +0 -310
  256. package/src/internal/cursor-account.integration.test.ts +0 -441
  257. package/src/internal/cursor-event-relay.test.ts +0 -746
  258. package/src/internal/cursor-github-credentials.integration.test.ts +0 -271
  259. package/src/internal/cursor-slack-relay.test.ts +0 -525
  260. package/src/internal/deploy-source.test.ts +0 -111
  261. package/src/internal/discovery.artifact-tool.test.ts +0 -136
  262. package/src/internal/discovery.artifacts.test.ts +0 -119
  263. package/src/internal/discovery.builtin-tools.test.ts +0 -94
  264. package/src/internal/discovery.concurrency.test.ts +0 -60
  265. package/src/internal/discovery.cursor-account.test.ts +0 -133
  266. package/src/internal/discovery.cwd.test.ts +0 -83
  267. package/src/internal/discovery.hosting.test.ts +0 -80
  268. package/src/internal/discovery.identity.test.ts +0 -44
  269. package/src/internal/docs-site.test.ts +0 -66
  270. package/src/internal/duration.test.ts +0 -29
  271. package/src/internal/eval-judge-model.test.ts +0 -187
  272. package/src/internal/eval-run-store.cancel.test.ts +0 -142
  273. package/src/internal/eval-run-store.storage.test.ts +0 -211
  274. package/src/internal/eval-runner.http.test.ts +0 -403
  275. package/src/internal/eval-runner.run.test.ts +0 -928
  276. package/src/internal/evals-client.test.ts +0 -307
  277. package/src/internal/event-mapper.test.ts +0 -243
  278. package/src/internal/github-fanout.test.ts +0 -213
  279. package/src/internal/handleAgentServeTrigger.test.ts +0 -185
  280. package/src/internal/host-kv.test.ts +0 -82
  281. package/src/internal/host-platforms.test.ts +0 -126
  282. package/src/internal/http-channel.test.ts +0 -402
  283. package/src/internal/init-project.test.ts +0 -270
  284. package/src/internal/install-cursor-skills.test.ts +0 -262
  285. package/src/internal/local-env.test.ts +0 -120
  286. package/src/internal/log-ring.test.ts +0 -31
  287. package/src/internal/logs-client.test.ts +0 -350
  288. package/src/internal/mcp-endpoint.test.ts +0 -436
  289. package/src/internal/mcp-host.test.ts +0 -298
  290. package/src/internal/mcp-oauth.test.ts +0 -148
  291. package/src/internal/net.test.ts +0 -17
  292. package/src/internal/peer-connections.test.ts +0 -128
  293. package/src/internal/peer-mcp.integration.test.ts +0 -289
  294. package/src/internal/playground/toolchain.test.ts +0 -53
  295. package/src/internal/playground-cli.test.ts +0 -187
  296. package/src/internal/playground-proxy.test.ts +0 -376
  297. package/src/internal/prompt-context.integration.test.ts +0 -232
  298. package/src/internal/prompt-context.test.ts +0 -127
  299. package/src/internal/reminder-runner.test.ts +0 -432
  300. package/src/internal/reminder-store.test.ts +0 -53
  301. package/src/internal/request-headers.test.ts +0 -27
  302. package/src/internal/resolve-prod-target.test.ts +0 -787
  303. package/src/internal/resolved-connections.test.ts +0 -295
  304. package/src/internal/router.test.ts +0 -57
  305. package/src/internal/sdk-runner.test.ts +0 -290
  306. package/src/internal/server.artifacts.test.ts +0 -334
  307. package/src/internal/session-engine.artifacts.test.ts +0 -293
  308. package/src/internal/session-engine.coalesce.test.ts +0 -169
  309. package/src/internal/session-engine.concurrency.test.ts +0 -250
  310. package/src/internal/session-engine.host-oauth-mcp.test.ts +0 -110
  311. package/src/internal/session-engine.interrupt.test.ts +0 -577
  312. package/src/internal/session-engine.storage.test.ts +0 -547
  313. package/src/internal/session-urls.test.ts +0 -28
  314. package/src/internal/sessions-client.test.ts +0 -518
  315. package/src/internal/storage-coordinator.test.ts +0 -517
  316. package/src/internal/tool-call.test.ts +0 -458
  317. package/src/internal/tool-result.test.ts +0 -52
  318. package/src/internal/trajectory.approvals.test.ts +0 -83
  319. package/src/internal/trajectory.subagents.test.ts +0 -198
  320. package/src/internal/turn-governor.test.ts +0 -137
  321. package/src/internal/update-check.test.ts +0 -485
  322. package/src/internal/workspace.test.ts +0 -207
  323. package/src/storage-backends/cursor-hosted.test.ts +0 -121
  324. /package/dist/docs/assets/{index.md.Dfv5ic9t.lean.js → index.md.CH_s5uZe.lean.js} +0 -0
@@ -1,391 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { SLACK_LOADING_MESSAGE_MAX_LEN } from "./constants.js";
3
- import {
4
- buildSlackManifest,
5
- computeStreamAppend,
6
- createSlackLogger,
7
- credentialsFromEnv,
8
- envPrefixFromName,
9
- extractGithubPrRef,
10
- parseAppMentionEvent,
11
- parseDirectMessageEvent,
12
- SLACK_EXTERNAL_APP_MANAGEMENT_URL,
13
- slackChannel,
14
- slackContinuationKey,
15
- slackEnvKeys,
16
- slackTextToMarkdown,
17
- slackThreadPermalink,
18
- truncateTypingStatus,
19
- tryResolveAppToken,
20
- tryResolveBotToken,
21
- } from "./index.js";
22
-
23
- describe("slackContinuationKey", () => {
24
- it("joins channel and thread", () => {
25
- expect(slackContinuationKey("C123", "1719.000")).toBe("C123:1719.000");
26
- });
27
- });
28
-
29
- describe("slackThreadPermalink", () => {
30
- it("prefers the client thread deep link when team is known", () => {
31
- expect(
32
- slackThreadPermalink({
33
- teamId: "T1",
34
- channelId: "C9",
35
- threadTs: "1783451265.725729",
36
- })
37
- ).toBe("https://app.slack.com/client/T1/C9/thread/C9-1783451265.725729");
38
- });
39
-
40
- it("falls back to archives permalink without team", () => {
41
- expect(
42
- slackThreadPermalink({
43
- channelId: "C9",
44
- threadTs: "1783451265.725729",
45
- })
46
- ).toBe(
47
- "https://app.slack.com/archives/C9/p1783451265725729?thread_ts=1783451265.725729"
48
- );
49
- });
50
- });
51
-
52
- describe("createSlackLogger", () => {
53
- it("formats levels and fields", () => {
54
- const lines: string[] = [];
55
- const log = createSlackLogger((line) => lines.push(line));
56
- log.info("inbound", { kind: "app_mention", channel: "C9" });
57
- log.warn("chat.startStream unavailable", { error: "missing_scope" });
58
- expect(lines[0]).toBe(
59
- "[agentkit/slack] inbound kind=app_mention channel=C9"
60
- );
61
- expect(lines[1]).toBe(
62
- "[agentkit/slack] warn chat.startStream unavailable error=missing_scope"
63
- );
64
- });
65
- });
66
-
67
- describe("inbound parsers", () => {
68
- it("parses app_mention", () => {
69
- const message = parseAppMentionEvent({
70
- type: "event_callback",
71
- team_id: "T1",
72
- event: {
73
- type: "app_mention",
74
- user: "U1",
75
- text: "<@Ubot> hi there",
76
- ts: "1.0",
77
- channel: "C9",
78
- },
79
- });
80
- expect(message).not.toBeNull();
81
- expect(message?.channelId).toBe("C9");
82
- expect(message?.threadTs).toBe("1.0");
83
- expect(message?.markdown).toContain("hi there");
84
- expect(message?.teamId).toBe("T1");
85
- });
86
-
87
- it("parses DMs and drops bot messages", () => {
88
- const dm = parseDirectMessageEvent({
89
- type: "event_callback",
90
- team_id: "T1",
91
- event: {
92
- type: "message",
93
- channel_type: "im",
94
- user: "U1",
95
- text: "hello",
96
- ts: "2.0",
97
- channel: "D1",
98
- },
99
- });
100
- expect(dm?.markdown).toBe("hello");
101
-
102
- const bot = parseDirectMessageEvent({
103
- type: "event_callback",
104
- event: {
105
- type: "message",
106
- channel_type: "im",
107
- bot_id: "B1",
108
- text: "noop",
109
- ts: "2.0",
110
- channel: "D1",
111
- },
112
- });
113
- expect(bot).toBeNull();
114
- });
115
-
116
- it("drops bot-authored app_mention events", () => {
117
- expect(
118
- parseAppMentionEvent({
119
- type: "event_callback",
120
- event: {
121
- type: "app_mention",
122
- bot_id: "B1",
123
- text: "<@Ubot> loop",
124
- ts: "1.0",
125
- channel: "C9",
126
- },
127
- })
128
- ).toBeNull();
129
- });
130
- });
131
-
132
- describe("slackTextToMarkdown", () => {
133
- it("strips user mentions", () => {
134
- expect(slackTextToMarkdown("<@U123> please help")).toBe(
135
- "@U123 please help"
136
- );
137
- });
138
- });
139
-
140
- describe("buildSlackManifest", () => {
141
- it("enables Socket Mode without request_url", () => {
142
- const manifest = buildSlackManifest({
143
- name: "Weather Bot",
144
- env: "dev",
145
- });
146
- expect(manifest.display_information.name).toBe("Weather Bot (dev)");
147
- expect(manifest.features.bot_user.display_name).toContain("dev");
148
- expect(manifest.settings.socket_mode_enabled).toBe(true);
149
- expect(
150
- (manifest.settings.event_subscriptions as { request_url?: string })
151
- .request_url
152
- ).toBeUndefined();
153
- expect(manifest.oauth_config.scopes.bot).toContain("chat:write");
154
- expect(manifest.settings.event_subscriptions.bot_events).toContain(
155
- "app_mention"
156
- );
157
- expect(manifest.settings.interactivity.is_enabled).toBe(false);
158
- });
159
-
160
- it("hides tokens and Slack-side installs, pointing management at Cursor", () => {
161
- for (const env of ["dev", "prod"] as const) {
162
- expect(
163
- buildSlackManifest({ name: "Weather Bot", env }).settings
164
- .managed_app_settings
165
- ).toEqual({
166
- is_install_from_slack_disabled: true,
167
- external_app_management_url: SLACK_EXTERNAL_APP_MANAGEMENT_URL,
168
- });
169
- }
170
- expect(SLACK_EXTERNAL_APP_MANAGEMENT_URL).toBe(
171
- "https://cursor.com/dashboard/deployed-agents"
172
- );
173
- });
174
-
175
- it("omits managed_app_settings for hand-imported manifests", () => {
176
- const manifest = buildSlackManifest({
177
- name: "Weather Bot",
178
- env: "prod",
179
- managedByCursor: false,
180
- });
181
- expect("managed_app_settings" in manifest.settings).toBe(false);
182
- });
183
-
184
- it("pins Benny's Slack app to benny-dev with org deploy", () => {
185
- for (const env of ["dev", "prod"] as const) {
186
- const manifest = buildSlackManifest({
187
- name: "Benny",
188
- env,
189
- channelPosts: true,
190
- });
191
- expect(manifest.display_information.name).toBe("benny-dev");
192
- expect(manifest.features.bot_user.display_name).toBe("benny-dev");
193
- expect(manifest.settings.org_deploy_enabled).toBe(true);
194
- expect(manifest.settings.socket_mode_enabled).toBe(true);
195
- expect(manifest.settings.token_rotation_enabled).toBe(false);
196
- expect(manifest.settings.is_mcp_enabled).toBe(false);
197
- }
198
- expect(
199
- buildSlackManifest({ name: "benny", env: "prod" }).features.bot_user
200
- .display_name
201
- ).toBe("benny-dev");
202
- });
203
-
204
- it("enables interactivity when opted in", () => {
205
- const manifest = buildSlackManifest({
206
- name: "Weather Bot",
207
- env: "prod",
208
- interactivity: true,
209
- });
210
- expect(manifest.settings.interactivity.is_enabled).toBe(true);
211
- });
212
-
213
- it("omits channel-post events by default and adds them when opted in", () => {
214
- const defaults = buildSlackManifest({ name: "Bot", env: "dev" });
215
- expect(defaults.settings.event_subscriptions.bot_events).not.toContain(
216
- "message.channels"
217
- );
218
-
219
- const watching = buildSlackManifest({
220
- name: "Bot",
221
- env: "dev",
222
- channelPosts: true,
223
- });
224
- expect(watching.settings.event_subscriptions.bot_events).toContain(
225
- "message.channels"
226
- );
227
- expect(watching.settings.event_subscriptions.bot_events).toContain(
228
- "message.groups"
229
- );
230
- });
231
-
232
- it("defaults the description from the name and honors an explicit one", () => {
233
- const bare = buildSlackManifest({ name: "Bot", env: "prod" });
234
- expect(bare.display_information.description).toBe(
235
- "Bot — an AI agent built with Cursor"
236
- );
237
- expect(bare.features.assistant_view?.assistant_description).toBe(
238
- "Bot — an AI agent built with Cursor"
239
- );
240
-
241
- const described = buildSlackManifest({
242
- name: "Bot",
243
- env: "prod",
244
- description: "Does things",
245
- });
246
- expect(described.display_information.description).toBe("Does things");
247
- expect(described.features.assistant_view?.assistant_description).toBe(
248
- "Does things"
249
- );
250
- });
251
-
252
- it("keeps the default description inside Slack's 140-char bound", () => {
253
- const manifest = buildSlackManifest({
254
- name: "x".repeat(200),
255
- env: "prod",
256
- });
257
- expect(manifest.display_information.description).toHaveLength(140);
258
- expect(manifest.display_information.description).toContain(
259
- "an AI agent built with Cursor"
260
- );
261
- });
262
- });
263
-
264
- describe("slackChannel engagement", () => {
265
- it("rejects an empty channelPosts allowlist", () => {
266
- expect(() =>
267
- slackChannel({
268
- engagement: { channelPosts: { allow: [" "] } },
269
- })
270
- ).toThrow(/allow must list at least one channel/);
271
- });
272
-
273
- it("surfaces watched channels in channel meta", () => {
274
- const channel = slackChannel({
275
- engagement: { channelPosts: { allow: ["#issues-glass", "C123"] } },
276
- });
277
- expect(channel.meta?.kind).toBe("slack");
278
- expect(channel.meta?.slackChannelPosts).toEqual(["#issues-glass", "C123"]);
279
-
280
- const plain = slackChannel();
281
- expect(plain.meta?.slackChannelPosts).toBeUndefined();
282
- });
283
- });
284
-
285
- describe("socket mode event payload", () => {
286
- it("parses app_mention from the event body Slack delivers over Socket Mode", () => {
287
- const message = parseAppMentionEvent({
288
- type: "event_callback",
289
- team_id: "T1",
290
- event_id: "EvSOCKET1",
291
- event: {
292
- type: "app_mention",
293
- user: "U1",
294
- text: "<@Ubot> via socket hello",
295
- ts: "9.1",
296
- channel: "C9",
297
- },
298
- });
299
- expect(message?.markdown).toContain("via socket hello");
300
- expect(message?.channelId).toBe("C9");
301
- });
302
- });
303
-
304
- describe("computeStreamAppend", () => {
305
- it("returns full text when nothing was posted", () => {
306
- expect(computeStreamAppend(null, "Hello")).toBe("Hello");
307
- expect(computeStreamAppend("", "Hello")).toBe("Hello");
308
- });
309
-
310
- it("returns the unsent suffix", () => {
311
- expect(computeStreamAppend("Hello", "Hello world")).toBe(" world");
312
- });
313
-
314
- it("returns empty when text does not extend lastPosted", () => {
315
- expect(computeStreamAppend("Hello", "Hi there")).toBe("");
316
- });
317
- });
318
-
319
- describe("truncateTypingStatus", () => {
320
- it("keeps loading_messages under Slack's 51-char limit", () => {
321
- const long = "a".repeat(80);
322
- const truncated = truncateTypingStatus(long);
323
- expect(truncated.length).toBe(SLACK_LOADING_MESSAGE_MAX_LEN);
324
- expect(truncated.endsWith("…")).toBe(true);
325
- });
326
- });
327
-
328
- describe("env prefixes", () => {
329
- it("normalizes agent names", () => {
330
- expect(envPrefixFromName("bugbot-cloud")).toBe("BUGBOT_CLOUD");
331
- expect(envPrefixFromName("security-reviewer")).toBe("SECURITY_REVIEWER");
332
- });
333
-
334
- it("builds per-agent env keys", () => {
335
- expect(slackEnvKeys("BUGBOT")).toEqual({
336
- bot: "BUGBOT_SLACK_BOT_TOKEN",
337
- app: "BUGBOT_SLACK_APP_TOKEN",
338
- });
339
- expect(slackEnvKeys()).toEqual({
340
- bot: "SLACK_BOT_TOKEN",
341
- app: "SLACK_APP_TOKEN",
342
- });
343
- });
344
-
345
- it("does not fall through to shared tokens for prefixed credentials", async () => {
346
- const previousBot = process.env.SLACK_BOT_TOKEN;
347
- const previousApp = process.env.SLACK_APP_TOKEN;
348
- process.env.SLACK_BOT_TOKEN = "xoxb-shared";
349
- process.env.SLACK_APP_TOKEN = "xapp-shared";
350
- delete process.env.BUGBOT_SLACK_BOT_TOKEN;
351
- delete process.env.BUGBOT_SLACK_APP_TOKEN;
352
- try {
353
- const credentials = credentialsFromEnv("BUGBOT");
354
- expect(await tryResolveBotToken(credentials)).toBeUndefined();
355
- expect(await tryResolveAppToken(credentials)).toBeUndefined();
356
- } finally {
357
- if (previousBot === undefined) {
358
- delete process.env.SLACK_BOT_TOKEN;
359
- } else {
360
- process.env.SLACK_BOT_TOKEN = previousBot;
361
- }
362
- if (previousApp === undefined) {
363
- delete process.env.SLACK_APP_TOKEN;
364
- } else {
365
- process.env.SLACK_APP_TOKEN = previousApp;
366
- }
367
- }
368
- });
369
- });
370
-
371
- describe("extractGithubPrRef", () => {
372
- it("parses URLs and owner/repo#N", () => {
373
- expect(
374
- extractGithubPrRef("please review https://github.com/acme/widget/pull/42")
375
- ).toEqual({
376
- owner: "acme",
377
- repo: "widget",
378
- number: 42,
379
- url: "https://github.com/acme/widget/pull/42",
380
- label: "acme/widget#42",
381
- });
382
- expect(extractGithubPrRef("drive acme/widget#7 for me")).toEqual({
383
- owner: "acme",
384
- repo: "widget",
385
- number: 7,
386
- url: "https://github.com/acme/widget/pull/7",
387
- label: "acme/widget#7",
388
- });
389
- expect(extractGithubPrRef("no pr here")).toBeUndefined();
390
- });
391
- });
@@ -1,23 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { isWholeMessageStop, stripLeadingSlackMentions } from "./stop.js";
3
-
4
- describe("isWholeMessageStop", () => {
5
- it("matches bare stop and @mention stop", () => {
6
- expect(isWholeMessageStop("stop")).toBe(true);
7
- expect(isWholeMessageStop("STOP")).toBe(true);
8
- expect(isWholeMessageStop("<@Ubot> stop")).toBe(true);
9
- expect(isWholeMessageStop("<@Ubot> stop ")).toBe(true);
10
- });
11
-
12
- it("does not match embedded stop", () => {
13
- expect(isWholeMessageStop("please stop investigating")).toBe(false);
14
- expect(isWholeMessageStop("<@Ubot> stop the deploy")).toBe(false);
15
- expect(isWholeMessageStop("")).toBe(false);
16
- });
17
- });
18
-
19
- describe("stripLeadingSlackMentions", () => {
20
- it("strips repeated leading mentions", () => {
21
- expect(stripLeadingSlackMentions("<@A> <@B> stop")).toBe("stop");
22
- });
23
- });
@@ -1,202 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import * as slackApi from "./api.js";
3
- import {
4
- findLastBotMessageTs,
5
- formatSlackThreadContextBlock,
6
- formatSlackThreadDeltaBlock,
7
- loadSlackThreadContextBlock,
8
- mergeThreadMessagesByTs,
9
- selectThreadDeltaMessages,
10
- trimThreadMessagesForPrompt,
11
- } from "./thread-context.js";
12
-
13
- describe("formatSlackThreadContextBlock", () => {
14
- it("formats parent then replies and excludes the triggering message", () => {
15
- const block = formatSlackThreadContextBlock(
16
- [
17
- {
18
- ts: "1.0",
19
- bot_id: "B1",
20
- username: "Datadog",
21
- text: "parent alert",
22
- },
23
- { ts: "2.0", user: "U1", text: "looking" },
24
- { ts: "3.0", user: "U2", text: "@jenny what happened?" },
25
- ],
26
- { excludeTs: "3.0" }
27
- );
28
- expect(block).toContain("<slack_thread>");
29
- expect(block).toContain("[parent] bot:B1 (Datadog) @ 1.0:");
30
- expect(block).toContain("parent alert");
31
- expect(block).toContain("[reply] U1 @ 2.0:");
32
- expect(block).not.toContain("what happened?");
33
- expect(block).toContain("</slack_thread>");
34
- });
35
-
36
- it("labels parent by thread ts even when earlier rows are filtered", () => {
37
- const block = formatSlackThreadContextBlock(
38
- [
39
- { ts: "1.0", user: "U1", text: "" },
40
- { ts: "2.0", user: "U2", text: "actual parent" },
41
- { ts: "3.0", user: "U3", text: "reply" },
42
- ],
43
- { threadTs: "2.0", excludeTs: "3.0" }
44
- );
45
- expect(block).toContain("[parent] U2 @ 2.0:");
46
- expect(block).not.toContain("[reply] U3 @ 3.0:");
47
- expect(block).not.toContain("[parent] U3");
48
- });
49
-
50
- it("keeps the thread root plus the newest replies when over the limit", () => {
51
- const messages = [{ ts: "1.0", user: "U0", text: "root" }];
52
- for (let i = 2; i <= 60; i++) {
53
- messages.push({ ts: `${i}.0`, user: "U1", text: `reply ${i}` });
54
- }
55
- const trimmed = trimThreadMessagesForPrompt(messages, {
56
- threadTs: "1.0",
57
- limit: 50,
58
- });
59
- expect(trimmed[0]).toMatchObject({ ts: "1.0" });
60
- expect(trimmed.at(-1)).toMatchObject({ ts: "60.0" });
61
- expect(trimmed).toHaveLength(50);
62
- });
63
-
64
- it("returns undefined when nothing usable remains", () => {
65
- expect(
66
- formatSlackThreadContextBlock([{ ts: "1.0", text: "only me" }], {
67
- excludeTs: "1.0",
68
- })
69
- ).toBeUndefined();
70
- });
71
-
72
- it("truncates oversized blocks", () => {
73
- const block = formatSlackThreadContextBlock(
74
- [
75
- {
76
- ts: "1.0",
77
- user: "U1",
78
- text: "x".repeat(500),
79
- },
80
- ],
81
- { maxChars: 120 }
82
- );
83
- expect(block).toBeDefined();
84
- expect(block!.length).toBeLessThanOrEqual(200);
85
- expect(block).toContain("truncated");
86
- expect(block).toContain("</slack_thread>");
87
- });
88
- });
89
-
90
- describe("thread delta helpers", () => {
91
- it("finds the latest bot message ts", () => {
92
- expect(
93
- findLastBotMessageTs([
94
- { ts: "1.0", user: "U1", text: "human" },
95
- { ts: "1.5", bot_id: "B1", text: "bot a" },
96
- { ts: "2.0", user: "U2", text: "human 2" },
97
- { ts: "2.5", bot_id: "B1", text: "bot b" },
98
- ])
99
- ).toBe("2.5");
100
- });
101
-
102
- it("restricts watermark to the agent bot when identity is provided", () => {
103
- expect(
104
- findLastBotMessageTs(
105
- [
106
- { ts: "1.0", bot_id: "BDD", text: "datadog" },
107
- { ts: "1.5", bot_id: "BJENNY", text: "jenny" },
108
- { ts: "2.0", user: "U1", text: "human" },
109
- { ts: "2.5", bot_id: "BDD", text: "datadog again" },
110
- ],
111
- { botId: "BJENNY" }
112
- )
113
- ).toBe("1.5");
114
- });
115
-
116
- it("merges missing triggering ask by ts", () => {
117
- const merged = mergeThreadMessagesByTs(
118
- [{ ts: "1.0", user: "U0", text: "root" }],
119
- [{ ts: "2.0", user: "U1", text: "lagging ask" }]
120
- );
121
- expect(merged.map((m) => m.ts)).toEqual(["1.0", "2.0"]);
122
- });
123
-
124
- it("formats slack_thread_delta after watermark", () => {
125
- const delta = selectThreadDeltaMessages(
126
- [
127
- { ts: "1.0", bot_id: "B1", text: "bot" },
128
- { ts: "2.0", user: "U1", text: "ask" },
129
- ],
130
- { afterTs: "1.0", excludeTs: "2.0" }
131
- );
132
- expect(delta).toHaveLength(0);
133
- const block = formatSlackThreadDeltaBlock(
134
- [
135
- { ts: "1.0", bot_id: "B1", text: "bot" },
136
- { ts: "2.0", user: "U1", text: "ask" },
137
- { ts: "3.0", user: "U2", text: "more" },
138
- ],
139
- { afterTs: "1.0", excludeTs: "2.0" }
140
- );
141
- expect(block).toContain("<slack_thread_delta>");
142
- expect(block).toContain("more");
143
- expect(block).not.toContain("ask");
144
- });
145
- });
146
-
147
- describe("loadSlackThreadContextBlock", () => {
148
- it("soft-fails without a bot token", async () => {
149
- await expect(
150
- loadSlackThreadContextBlock({
151
- botToken: undefined,
152
- channelId: "C1",
153
- threadTs: "1.0",
154
- })
155
- ).resolves.toBeUndefined();
156
- });
157
-
158
- it("calls conversations.replies and formats the result", async () => {
159
- const spy = vi.spyOn(slackApi, "callSlackApiSoft").mockResolvedValue({
160
- ok: true,
161
- messages: [
162
- { ts: "1.0", user: "Ubot", text: "alert body" },
163
- { ts: "2.0", user: "U1", text: "mention" },
164
- ],
165
- has_more: false,
166
- });
167
- const block = await loadSlackThreadContextBlock({
168
- botToken: "xoxb-test",
169
- channelId: "C1",
170
- threadTs: "1.0",
171
- excludeTs: "2.0",
172
- });
173
- expect(spy).toHaveBeenCalledWith(
174
- expect.objectContaining({
175
- operation: "conversations.replies",
176
- body: expect.objectContaining({
177
- channel: "C1",
178
- ts: "1.0",
179
- inclusive: true,
180
- }),
181
- })
182
- );
183
- expect(block).toContain("alert body");
184
- expect(block).not.toContain("mention");
185
- spy.mockRestore();
186
- });
187
-
188
- it("soft-fails when Slack returns not-ok", async () => {
189
- const spy = vi.spyOn(slackApi, "callSlackApiSoft").mockResolvedValue({
190
- ok: false,
191
- error: "channel_not_found",
192
- });
193
- await expect(
194
- loadSlackThreadContextBlock({
195
- botToken: "xoxb-test",
196
- channelId: "C1",
197
- threadTs: "1.0",
198
- })
199
- ).resolves.toBeUndefined();
200
- spy.mockRestore();
201
- });
202
- });