@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
@@ -17,6 +17,7 @@ import {
17
17
  parseSlackEvalDirective,
18
18
  } from "./eval-directive.js";
19
19
  import type { EventIdDedupe } from "./event-id-dedupe.js";
20
+ import { isExternalSlackSender } from "./external-policy.js";
20
21
  import {
21
22
  formatSlackContextBlock,
22
23
  parseAppMentionEvent,
@@ -91,6 +92,11 @@ export interface SlackInboundDispatchDeps extends SlackContinuationProbes {
91
92
  evalCommandsEnabled?: boolean;
92
93
  /** cursorAccount agent name stripped before `eval` directive parsing. */
93
94
  cursorAgentName?: string;
95
+ /**
96
+ * Drop external senders before the handler. Defaults to `true`; pass
97
+ * `false` to admit them (or when an upstream gate already refused them).
98
+ */
99
+ blockExternals?: boolean;
94
100
  }
95
101
 
96
102
  function triggeringMergeMessage(
@@ -132,6 +138,7 @@ export async function dispatchInboundMessage(args: {
132
138
  * (`OncallBuddy eval list`).
133
139
  */
134
140
  cursorAgentName?: string;
141
+ blockExternals?: boolean;
135
142
  }): Promise<void> {
136
143
  const { message, kind } = args;
137
144
  const log = args.log ?? defaultSlackLog;
@@ -142,7 +149,7 @@ export async function dispatchInboundMessage(args: {
142
149
  host: args.host,
143
150
  artifacts: args.artifacts,
144
151
  log,
145
- ...(message.teamId === undefined ? {} : { teamId: message.teamId }),
152
+ teamId: message.teamId,
146
153
  });
147
154
 
148
155
  log.info("inbound", {
@@ -155,6 +162,38 @@ export async function dispatchInboundMessage(args: {
155
162
  team: message.teamId,
156
163
  });
157
164
 
165
+ if (args.blockExternals !== false && message.author?.isBot !== true) {
166
+ const userId = message.author?.userId ?? "";
167
+ const teamId = message.teamId;
168
+ if (teamId === undefined || teamId === "") {
169
+ log.info("inbound dropped (missing team)", {
170
+ kind,
171
+ channel: message.channelId,
172
+ thread_ts: message.threadTs,
173
+ user: userId,
174
+ event_id: args.eventId,
175
+ });
176
+ return;
177
+ }
178
+ if (
179
+ await isExternalSlackSender({
180
+ botToken: args.credentials?.botToken,
181
+ slackTeamId: teamId,
182
+ userId,
183
+ })
184
+ ) {
185
+ log.info("inbound dropped (external sender)", {
186
+ kind,
187
+ channel: message.channelId,
188
+ thread_ts: message.threadTs,
189
+ user: userId,
190
+ team: teamId,
191
+ event_id: args.eventId,
192
+ });
193
+ return;
194
+ }
195
+ }
196
+
158
197
  const continuation = slackContinuationKey(
159
198
  message.channelId,
160
199
  message.threadTs
@@ -463,6 +502,7 @@ export function dispatchInboundFromEventBody(
463
502
  log,
464
503
  evalCommandsEnabled: deps.evalCommandsEnabled,
465
504
  cursorAgentName: deps.cursorAgentName,
505
+ blockExternals: deps.blockExternals,
466
506
  ...(deps.hasContinuationSession === undefined
467
507
  ? {}
468
508
  : { hasContinuationSession: deps.hasContinuationSession }),
@@ -504,6 +544,7 @@ export function dispatchInboundFromEventBody(
504
544
  log,
505
545
  evalCommandsEnabled: deps.evalCommandsEnabled,
506
546
  cursorAgentName: deps.cursorAgentName,
547
+ blockExternals: deps.blockExternals,
507
548
  ...(deps.hasContinuationSession === undefined
508
549
  ? {}
509
550
  : { hasContinuationSession: deps.hasContinuationSession }),
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Workspace-membership checks for AgentKit Slack agents.
3
+ * Mirrors backend `isExternalSlackUser` (cloud-agent/slack/externalChannelPolicy).
4
+ */
5
+
6
+ import { callSlackApi } from "./api.js";
7
+ import type { SlackBotToken } from "./types.js";
8
+
9
+ type SlackUserInfo = {
10
+ team_id?: string;
11
+ is_stranger?: boolean;
12
+ is_restricted?: boolean;
13
+ is_ultra_restricted?: boolean;
14
+ enterprise_user?: { teams?: string[] };
15
+ };
16
+
17
+ /** Pure classifier over a `users.info` user object. Fails open to external. */
18
+ export function classifySlackUserExternality(args: {
19
+ slackTeamId: string;
20
+ user: SlackUserInfo;
21
+ }): boolean {
22
+ const { slackTeamId, user } = args;
23
+ if (
24
+ user.is_stranger === true ||
25
+ user.is_restricted === true ||
26
+ user.is_ultra_restricted === true
27
+ ) {
28
+ return true;
29
+ }
30
+ return (
31
+ user.team_id !== undefined &&
32
+ user.team_id !== slackTeamId &&
33
+ !(user.enterprise_user?.teams ?? []).includes(slackTeamId)
34
+ );
35
+ }
36
+
37
+ /** Lookup + classify. Empty userId / API failure → external. */
38
+ export async function isExternalSlackSender(args: {
39
+ botToken: SlackBotToken | undefined;
40
+ slackTeamId: string;
41
+ userId: string;
42
+ }): Promise<boolean> {
43
+ if (args.userId === "") {
44
+ return true;
45
+ }
46
+ try {
47
+ const result = await callSlackApi({
48
+ botToken: args.botToken,
49
+ operation: "users.info",
50
+ body: { user: args.userId },
51
+ });
52
+ if (result.ok !== true || result.user === undefined) {
53
+ return true;
54
+ }
55
+ return classifySlackUserExternality({
56
+ slackTeamId: args.slackTeamId,
57
+ user: result.user as SlackUserInfo,
58
+ });
59
+ } catch {
60
+ return true;
61
+ }
62
+ }
@@ -33,6 +33,7 @@ export * from "./dispatch.js";
33
33
  export * from "./doctor.js";
34
34
  export * from "./eval-directive.js";
35
35
  export * from "./event-id-dedupe.js";
36
+ export * from "./external-policy.js";
36
37
  export * from "./inbound.js";
37
38
  export * from "./init.js";
38
39
  export * from "./interactive.js";
@@ -20,6 +20,7 @@ import { updateMessage } from "./api.js";
20
20
  import { decisionFromActionId, parseApprovalActionValue } from "./approvals.js";
21
21
  import { buildSlackInteractiveApprovalAuth } from "./auth.js";
22
22
  import { dispatchInboundMessage } from "./dispatch.js";
23
+ import { isExternalSlackSender } from "./external-policy.js";
23
24
  import { defaultSlackLog, type SlackLog } from "./log.js";
24
25
  import type {
25
26
  SlackBlockAction,
@@ -41,6 +42,8 @@ export interface SlackInteractiveDispatchDeps {
41
42
  ) => Promise<PendingApproval>;
42
43
  waitUntil: (promise: Promise<unknown>) => void;
43
44
  log?: SlackLog;
45
+ /** Defaults to true. */
46
+ blockExternals?: boolean;
44
47
  }
45
48
 
46
49
  /**
@@ -116,11 +119,35 @@ export async function dispatchInteractiveFromPayload(
116
119
  }
117
120
 
118
121
  const teamId = attrs.team_id;
122
+ if (deps.blockExternals !== false && parsed.userId !== "") {
123
+ if (teamId === undefined || teamId === "") {
124
+ log.info("interactive approval dropped (missing team)", {
125
+ session: value.sessionId,
126
+ user: parsed.userId,
127
+ });
128
+ return true;
129
+ }
130
+ if (
131
+ await isExternalSlackSender({
132
+ botToken: deps.credentials?.botToken,
133
+ slackTeamId: teamId,
134
+ userId: parsed.userId,
135
+ })
136
+ ) {
137
+ log.info("interactive approval dropped (external sender)", {
138
+ session: value.sessionId,
139
+ user: parsed.userId,
140
+ team: teamId,
141
+ });
142
+ return true;
143
+ }
144
+ }
145
+
119
146
  const clickingAuth = buildSlackInteractiveApprovalAuth({
120
147
  sessionAuth,
121
148
  channelId: parsed.channelId || sessionChannel,
122
149
  threadTs: parsed.threadTs || sessionThread,
123
- ...(teamId === undefined || teamId === "" ? {} : { teamId }),
150
+ teamId: teamId === undefined || teamId === "" ? undefined : teamId,
124
151
  clickingUserId: parsed.userId,
125
152
  });
126
153
 
@@ -204,12 +231,9 @@ export function parseSlackBlockAction(
204
231
  : typeof user.name === "string"
205
232
  ? user.name
206
233
  : undefined;
207
- const teamId =
208
- typeof team.id === "string"
209
- ? team.id
210
- : typeof user.team_id === "string"
211
- ? user.team_id
212
- : undefined;
234
+ // Payload `team.id` only — never `user.team_id` (Connect home team).
235
+ // Membership gates must baseline the install workspace separately.
236
+ const teamId = typeof team.id === "string" ? team.id : undefined;
213
237
  const label =
214
238
  typeof action.text === "object" &&
215
239
  action.text !== null &&
@@ -278,6 +302,10 @@ export interface SlackBlockActionDispatchDeps extends SlackContinuationProbes {
278
302
  host: HostContext;
279
303
  artifacts: ArtifactsApi;
280
304
  log?: SlackLog;
305
+ /** Defaults to true. */
306
+ blockExternals?: boolean;
307
+ /** Install workspace from `auth.test`; baseline for the external gate. */
308
+ installTeamId?: string;
281
309
  }
282
310
 
283
311
  /**
@@ -285,7 +313,10 @@ export interface SlackBlockActionDispatchDeps extends SlackContinuationProbes {
285
313
  * `action_ts` (unique per click, never a real thread message), so ask-dedupe
286
314
  * claims and `<slack_thread>` exclusions stay per-click.
287
315
  */
288
- function blockActionAsMessage(action: SlackBlockAction): SlackMessage {
316
+ function blockActionAsMessage(
317
+ action: SlackBlockAction,
318
+ installTeamId: string | undefined
319
+ ): SlackMessage {
289
320
  const text = `[clicked: ${action.label ?? action.actionId}]`;
290
321
  return {
291
322
  text,
@@ -294,7 +325,7 @@ function blockActionAsMessage(action: SlackBlockAction): SlackMessage {
294
325
  ts: action.actionTs,
295
326
  threadTs: action.threadTs,
296
327
  channelId: action.channelId,
297
- teamId: action.teamId,
328
+ teamId: installTeamId,
298
329
  author: {
299
330
  userId: action.userId,
300
331
  userName: action.userName,
@@ -344,7 +375,7 @@ export async function dispatchBlockAction(
344
375
 
345
376
  await dispatchInboundMessage({
346
377
  kind: "block_action",
347
- message: blockActionAsMessage(action),
378
+ message: blockActionAsMessage(action, deps.installTeamId),
348
379
  handler: (ctx) => deps.handler(ctx, action),
349
380
  send: deps.send,
350
381
  credentials: deps.credentials,
@@ -353,6 +384,7 @@ export async function dispatchBlockAction(
353
384
  log,
354
385
  // Click text is synthetic — never treat it as a stop/eval directive.
355
386
  evalCommandsEnabled: false,
387
+ blockExternals: deps.blockExternals,
356
388
  hasContinuationSession: deps.hasContinuationSession,
357
389
  isContinuationBusy: deps.isContinuationBusy,
358
390
  getContinuationLastBotMessageTs: deps.getContinuationLastBotMessageTs,
@@ -43,6 +43,7 @@ import {
43
43
  buildDefaultEvents,
44
44
  createDefaultOnChannelPost,
45
45
  createDefaultOnInbound,
46
+ resolveSlackBlockExternals,
46
47
  } from "./defaults.js";
47
48
  import {
48
49
  dispatchInboundFromEventBody,
@@ -152,11 +153,14 @@ export function slackChannel<const Name extends string = string>(
152
153
  : (options.credentials ?? credentialsFromEnv(options.envPrefix));
153
154
  const envKeys = slackEnvKeys(options.envPrefix);
154
155
  const loadingMessages = options.loadingMessages;
155
- const defaultInbound = createDefaultOnInbound(loadingMessages);
156
+ const policy = options.policy;
157
+ const blockExternals = resolveSlackBlockExternals(policy);
158
+ const defaultInbound = createDefaultOnInbound(loadingMessages, policy);
156
159
  const onAppMention = options.onAppMention ?? defaultInbound;
157
160
  const onDirectMessage = options.onDirectMessage ?? defaultInbound;
158
161
  const onChannelPost =
159
- options.onChannelPost ?? createDefaultOnChannelPost(loadingMessages);
162
+ options.onChannelPost ??
163
+ createDefaultOnChannelPost(loadingMessages, policy);
160
164
  const engagement = options.engagement ?? {};
161
165
  const mentionsEnabled = engagement.mentions !== false;
162
166
  const directMessagesEnabled = engagement.directMessages !== false;
@@ -237,6 +241,8 @@ export function slackChannel<const Name extends string = string>(
237
241
  mentionsEnabled,
238
242
  directMessagesEnabled,
239
243
  evalCommandsEnabled,
244
+ // cursorAccount: backend router already refuses externals.
245
+ blockExternals: cursorAccount ? false : blockExternals,
240
246
  ...(args.hasContinuationSession === undefined
241
247
  ? {}
242
248
  : { hasContinuationSession: args.hasContinuationSession }),
@@ -329,10 +335,15 @@ export function slackChannel<const Name extends string = string>(
329
335
 
330
336
  let botUserId: string | undefined;
331
337
  let botId: string | undefined;
338
+ let installTeamId: string | undefined;
332
339
  let webClient: InstanceType<
333
340
  typeof import("@slack/web-api").WebClient
334
341
  > | null = null;
335
- if (channelPosts !== undefined || botMentions !== undefined) {
342
+ if (
343
+ channelPosts !== undefined ||
344
+ botMentions !== undefined ||
345
+ routeInteractive
346
+ ) {
336
347
  try {
337
348
  const { WebClient } = await import("@slack/web-api");
338
349
  webClient = new WebClient(botToken);
@@ -341,6 +352,8 @@ export function slackChannel<const Name extends string = string>(
341
352
  typeof identity.user_id === "string" ? identity.user_id : undefined;
342
353
  botId =
343
354
  typeof identity.bot_id === "string" ? identity.bot_id : undefined;
355
+ installTeamId =
356
+ typeof identity.team_id === "string" ? identity.team_id : undefined;
344
357
  } catch (error) {
345
358
  getLog().error("bot identity lookup degraded", {
346
359
  error: error instanceof Error ? error.message : String(error),
@@ -416,6 +429,7 @@ export function slackChannel<const Name extends string = string>(
416
429
  artifacts: args.artifacts,
417
430
  log: getLog(),
418
431
  evalCommandsEnabled,
432
+ blockExternals,
419
433
  ...(args.hasContinuationSession === undefined
420
434
  ? {}
421
435
  : {
@@ -491,6 +505,7 @@ export function slackChannel<const Name extends string = string>(
491
505
  resolveApproval: args.resolveApproval,
492
506
  waitUntil: args.waitUntil,
493
507
  log: getLog(),
508
+ blockExternals,
494
509
  }
495
510
  );
496
511
  if (handledAsApproval || onBlockAction === undefined) {
@@ -503,6 +518,8 @@ export function slackChannel<const Name extends string = string>(
503
518
  host: args.host,
504
519
  artifacts: args.artifacts,
505
520
  log: getLog(),
521
+ blockExternals,
522
+ installTeamId,
506
523
  hasContinuationSession: args.hasContinuationSession,
507
524
  isContinuationBusy: args.isContinuationBusy,
508
525
  getContinuationLastBotMessageTs:
@@ -432,8 +432,20 @@ export function assertSlackCursorAgentName(name: string): void {
432
432
  }
433
433
  }
434
434
 
435
+ /** `"thread"` = anyone in the thread (default); `"author"` = session starter only. */
436
+ export type SlackRespondTo = "thread" | "author";
437
+
438
+ export interface SlackAccessPolicy {
439
+ /** Defaults to `"thread"`. Applied by the default inbound handlers. */
440
+ respondTo?: SlackRespondTo;
441
+ /** Refuse Slack Connect / guest / other-workspace senders. Defaults to `true`. */
442
+ blockExternals?: boolean;
443
+ }
444
+
435
445
  export interface SlackChannelOptions {
436
446
  credentials?: SlackCredentials;
447
+ /** Defaults to `{ respondTo: "thread", blockExternals: true }`. */
448
+ policy?: SlackAccessPolicy;
437
449
  /**
438
450
  * Per-agent env prefix for tokens. `envPrefix: "MY_AGENT"` reads
439
451
  * `MY_AGENT_SLACK_BOT_TOKEN` + `MY_AGENT_SLACK_APP_TOKEN`. Prefer this over
@@ -187,6 +187,10 @@ export function applyBuiltinMetrics(
187
187
  m.cacheReadTokens += usage.cacheReadTokens;
188
188
  m.cacheWriteTokens += usage.cacheWriteTokens;
189
189
  }
190
+ const cost = event.data.cost;
191
+ if (cost !== undefined) {
192
+ m.costUsd += cost.totalUsd;
193
+ }
190
194
  addWallTime(accum, event.at);
191
195
  break;
192
196
  }
@@ -323,6 +323,7 @@ function addMetrics(target: ABMetrics, delta: ABMetrics): void {
323
323
  target.outputTokens += delta.outputTokens;
324
324
  target.cacheReadTokens += delta.cacheReadTokens;
325
325
  target.cacheWriteTokens += delta.cacheWriteTokens;
326
+ target.costUsd += delta.costUsd;
326
327
  target.wallTimeMs += delta.wallTimeMs;
327
328
  for (const [key, value] of Object.entries(delta.custom)) {
328
329
  target.custom[key] = (target.custom[key] ?? 0) + value;