@cursor/july 0.1.96 → 0.1.97

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 (321) hide show
  1. package/README.md +3 -2
  2. package/dist/bin/agent-serve.js +14 -3
  3. package/dist/channels/origin/api.d.ts +15 -4
  4. package/dist/channels/origin/api.d.ts.map +1 -1
  5. package/dist/channels/origin/api.js +19 -3
  6. package/dist/channels/origin/binding.d.ts +11 -0
  7. package/dist/channels/origin/binding.d.ts.map +1 -1
  8. package/dist/channels/origin/binding.js +41 -25
  9. package/dist/channels/origin/index.d.ts +1 -0
  10. package/dist/channels/origin/index.d.ts.map +1 -1
  11. package/dist/channels/origin/index.js +1 -0
  12. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  13. package/dist/channels/origin/origin-channel.js +3 -0
  14. package/dist/channels/origin/reactions.d.ts +56 -0
  15. package/dist/channels/origin/reactions.d.ts.map +1 -0
  16. package/dist/channels/origin/reactions.js +67 -0
  17. package/dist/channels/origin/types.d.ts +11 -0
  18. package/dist/channels/origin/types.d.ts.map +1 -1
  19. package/dist/channels/slack/approvals.d.ts +16 -0
  20. package/dist/channels/slack/approvals.d.ts.map +1 -1
  21. package/dist/channels/slack/approvals.js +86 -0
  22. package/dist/channels/slack/cli-project.d.ts +19 -0
  23. package/dist/channels/slack/cli-project.d.ts.map +1 -0
  24. package/dist/channels/slack/cli-project.js +234 -0
  25. package/dist/channels/slack/defaults.d.ts.map +1 -1
  26. package/dist/channels/slack/defaults.js +97 -39
  27. package/dist/channels/slack/init.d.ts +3 -2
  28. package/dist/channels/slack/init.d.ts.map +1 -1
  29. package/dist/channels/slack/init.js +5 -5
  30. package/dist/channels/slack/interactive.d.ts +2 -1
  31. package/dist/channels/slack/interactive.d.ts.map +1 -1
  32. package/dist/channels/slack/interactive.js +15 -4
  33. package/dist/channels/slack/manifest.d.ts +1 -1
  34. package/dist/channels/slack/manifest.js +1 -1
  35. package/dist/channels/slack/setup.d.ts +1 -1
  36. package/dist/channels/slack/setup.d.ts.map +1 -1
  37. package/dist/channels/slack/setup.js +10 -6
  38. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  39. package/dist/channels/slack/slack-channel.js +1 -0
  40. package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
  41. package/dist/channels/slack/tool-approval-events.js +91 -1
  42. package/dist/docs/404.html +2 -2
  43. package/dist/docs/ab.html +4 -4
  44. package/dist/docs/assets/{app.jXleCmOc.js → app.B3rWNYE1.js} +1 -1
  45. package/dist/docs/assets/chunks/@localSearchIndexroot.BU9nHdnQ.js +1 -0
  46. package/dist/docs/assets/chunks/{VPLocalSearchBox.8LhyglIT.js → VPLocalSearchBox.C1lhqJJR.js} +1 -1
  47. package/dist/docs/assets/chunks/{theme.9ged-pr2.js → theme.B40_SXuv.js} +2 -2
  48. package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
  49. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
  50. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
  51. package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
  52. package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
  53. package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
  54. package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
  55. package/dist/docs/assets/{reference_cli.md.CCnG-nJx.js → reference_cli.md.RyZf5OTE.js} +9 -6
  56. package/dist/docs/assets/{reference_cli.md.CCnG-nJx.lean.js → reference_cli.md.RyZf5OTE.lean.js} +1 -1
  57. package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
  58. package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.5zOAbV86.js} +1 -1
  59. package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
  60. package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.B84gw9Ii.js} +1 -1
  61. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
  62. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
  63. package/dist/docs/assets/troubleshooting.md.DCiPBhYs.js +1 -0
  64. package/dist/docs/building-with-agents.html +4 -4
  65. package/dist/docs/concepts.html +4 -4
  66. package/dist/docs/deployment.html +7 -7
  67. package/dist/docs/deployment.md +5 -4
  68. package/dist/docs/evals.html +4 -4
  69. package/dist/docs/guides/agent-to-agent.html +4 -4
  70. package/dist/docs/guides/cloud-runtime.html +4 -4
  71. package/dist/docs/guides/convert-automation.html +4 -4
  72. package/dist/docs/guides/github.html +4 -4
  73. package/dist/docs/guides/human-in-the-loop.html +4 -4
  74. package/dist/docs/guides/mcp-oauth.html +8 -8
  75. package/dist/docs/guides/mcp-oauth.md +26 -8
  76. package/dist/docs/guides/opentelemetry.html +4 -4
  77. package/dist/docs/guides/slack.html +17 -33
  78. package/dist/docs/guides/slack.md +92 -201
  79. package/dist/docs/guides/webhooks.html +4 -4
  80. package/dist/docs/hashmap.json +1 -1
  81. package/dist/docs/hillclimbing.html +4 -4
  82. package/dist/docs/index.html +6 -6
  83. package/dist/docs/index.md +5 -3
  84. package/dist/docs/llms-full.txt +299 -653
  85. package/dist/docs/llms.txt +2 -2
  86. package/dist/docs/quickstart.html +4 -4
  87. package/dist/docs/reference/agent-config.html +6 -6
  88. package/dist/docs/reference/agent-config.md +2 -2
  89. package/dist/docs/reference/artifacts.html +4 -4
  90. package/dist/docs/reference/channels.html +4 -4
  91. package/dist/docs/reference/cli.html +13 -10
  92. package/dist/docs/reference/cli.md +38 -16
  93. package/dist/docs/reference/connections.html +9 -8
  94. package/dist/docs/reference/connections.md +6 -3
  95. package/dist/docs/reference/hooks.html +4 -4
  96. package/dist/docs/reference/http-api.html +6 -6
  97. package/dist/docs/reference/http-api.md +2 -1
  98. package/dist/docs/reference/instructions.html +4 -4
  99. package/dist/docs/reference/playground.html +4 -4
  100. package/dist/docs/reference/project-layout.html +4 -4
  101. package/dist/docs/reference/prompt.html +4 -4
  102. package/dist/docs/reference/schedules.html +4 -4
  103. package/dist/docs/reference/sessions.html +5 -5
  104. package/dist/docs/reference/sessions.md +4 -2
  105. package/dist/docs/reference/skills.html +4 -4
  106. package/dist/docs/reference/subagents.html +4 -4
  107. package/dist/docs/reference/tools.html +6 -6
  108. package/dist/docs/reference/tools.md +9 -2
  109. package/dist/docs/scaffolding-agents.html +4 -4
  110. package/dist/docs/storage.html +4 -4
  111. package/dist/docs/templates/agentic-owners.html +4 -4
  112. package/dist/docs/templates/agents-md.html +28 -0
  113. package/dist/docs/templates/agents-md.md +105 -0
  114. package/dist/docs/templates/code-wiki.html +5 -5
  115. package/dist/docs/templates/demo.html +4 -4
  116. package/dist/docs/templates/pr-autofixer.html +4 -4
  117. package/dist/docs/templates/security-help.html +4 -4
  118. package/dist/docs/templates/security-reviewer.html +5 -5
  119. package/dist/docs/templates/triage.html +4 -4
  120. package/dist/docs/troubleshooting.html +5 -5
  121. package/dist/docs/troubleshooting.md +2 -2
  122. package/dist/evals/run-facts.d.ts.map +1 -1
  123. package/dist/evals/run-facts.js +27 -0
  124. package/dist/internal/advertise-tools.d.ts.map +1 -1
  125. package/dist/internal/advertise-tools.js +15 -9
  126. package/dist/internal/approval-gate.d.ts +12 -13
  127. package/dist/internal/approval-gate.d.ts.map +1 -1
  128. package/dist/internal/approval-gate.js +16 -21
  129. package/dist/internal/chat-client.js +10 -0
  130. package/dist/internal/cli-deploy.d.ts +5 -0
  131. package/dist/internal/cli-deploy.d.ts.map +1 -1
  132. package/dist/internal/cli-deploy.js +50 -17
  133. package/dist/internal/cli-mcp-oauth.js +8 -18
  134. package/dist/internal/cli-slack-manual.d.ts +24 -2
  135. package/dist/internal/cli-slack-manual.d.ts.map +1 -1
  136. package/dist/internal/cli-slack-manual.js +251 -20
  137. package/dist/internal/cli-slack.d.ts +9 -2
  138. package/dist/internal/cli-slack.d.ts.map +1 -1
  139. package/dist/internal/cli-slack.js +29 -82
  140. package/dist/internal/cursor/account-mcp.d.ts +1 -0
  141. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  142. package/dist/internal/cursor/account-mcp.js +17 -0
  143. package/dist/internal/cursor-agent-template.d.ts +1 -1
  144. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  145. package/dist/internal/cursor-agent-template.js +1 -0
  146. package/dist/internal/deploy-client.d.ts +17 -0
  147. package/dist/internal/deploy-client.d.ts.map +1 -1
  148. package/dist/internal/deploy-client.js +31 -10
  149. package/dist/internal/env-file.d.ts +19 -0
  150. package/dist/internal/env-file.d.ts.map +1 -0
  151. package/dist/internal/env-file.js +90 -0
  152. package/dist/internal/framework-file-storage.d.ts +1 -1
  153. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  154. package/dist/internal/framework-file-storage.js +6 -0
  155. package/dist/internal/hosted-delivery-protocol.d.ts +7 -0
  156. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  157. package/dist/internal/hosted-delivery-protocol.js +7 -0
  158. package/dist/internal/hosted-delivery.d.ts +6 -0
  159. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  160. package/dist/internal/hosted-delivery.js +63 -22
  161. package/dist/internal/hosted-execution-diag.d.ts +48 -0
  162. package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
  163. package/dist/internal/hosted-execution-diag.js +150 -0
  164. package/dist/internal/http-channel.d.ts.map +1 -1
  165. package/dist/internal/http-channel.js +81 -1
  166. package/dist/internal/init-project.d.ts.map +1 -1
  167. package/dist/internal/init-project.js +2 -0
  168. package/dist/internal/logs-client.d.ts +5 -0
  169. package/dist/internal/logs-client.d.ts.map +1 -1
  170. package/dist/internal/logs-client.js +5 -0
  171. package/dist/internal/mcp-endpoint.js +2 -3
  172. package/dist/internal/mcp-host.d.ts +10 -0
  173. package/dist/internal/mcp-host.d.ts.map +1 -1
  174. package/dist/internal/mcp-host.js +57 -8
  175. package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
  176. package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
  177. package/dist/internal/mcp-oauth-on-demand.js +287 -0
  178. package/dist/internal/mcp-oauth-persist.d.ts +42 -0
  179. package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
  180. package/dist/internal/mcp-oauth-persist.js +92 -0
  181. package/dist/internal/mcp-oauth.d.ts +1 -0
  182. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  183. package/dist/internal/mcp-oauth.js +10 -2
  184. package/dist/internal/otel/collector.d.ts.map +1 -1
  185. package/dist/internal/otel/collector.js +22 -9
  186. package/dist/internal/server.d.ts +3 -0
  187. package/dist/internal/server.d.ts.map +1 -1
  188. package/dist/internal/server.js +49 -22
  189. package/dist/internal/session-engine.d.ts +88 -3
  190. package/dist/internal/session-engine.d.ts.map +1 -1
  191. package/dist/internal/session-engine.js +515 -43
  192. package/dist/internal/sessions-client.d.ts +9 -7
  193. package/dist/internal/sessions-client.d.ts.map +1 -1
  194. package/dist/internal/sessions-client.js +3 -1
  195. package/dist/internal/storage-coordinator.d.ts +7 -0
  196. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  197. package/dist/internal/storage-coordinator.js +28 -5
  198. package/dist/internal/storage-roles.d.ts +5 -0
  199. package/dist/internal/storage-roles.d.ts.map +1 -1
  200. package/dist/internal/stream-progress.d.ts.map +1 -1
  201. package/dist/internal/stream-progress.js +10 -0
  202. package/dist/internal/tool-policy.d.ts +9 -1
  203. package/dist/internal/tool-policy.d.ts.map +1 -1
  204. package/dist/internal/tool-policy.js +13 -0
  205. package/dist/internal/trajectory.d.ts.map +1 -1
  206. package/dist/internal/trajectory.js +10 -0
  207. package/dist/internal/turn-outbox.d.ts +24 -0
  208. package/dist/internal/turn-outbox.d.ts.map +1 -0
  209. package/dist/internal/turn-outbox.js +80 -0
  210. package/dist/playground/assets/{index-Cx9CclK_.js → index-DrkI6y5O.js} +45 -45
  211. package/dist/playground/assets/index-DzNGwm7q.css +1 -0
  212. package/dist/playground/index.html +2 -2
  213. package/dist/storage-backends/cursor-hosted.d.ts +1 -1
  214. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  215. package/dist/storage-backends/cursor-hosted.js +12 -0
  216. package/dist/storage.d.ts +8 -0
  217. package/dist/storage.d.ts.map +1 -1
  218. package/dist/types.d.ts +63 -7
  219. package/dist/types.d.ts.map +1 -1
  220. package/docs/README.md +5 -3
  221. package/docs/deployment.md +5 -4
  222. package/docs/guides/mcp-oauth.md +26 -8
  223. package/docs/guides/slack.md +93 -202
  224. package/docs/reference/agent-config.md +2 -2
  225. package/docs/reference/cli.md +38 -16
  226. package/docs/reference/connections.md +6 -3
  227. package/docs/reference/http-api.md +2 -1
  228. package/docs/reference/sessions.md +4 -2
  229. package/docs/reference/tools.md +9 -2
  230. package/docs/templates/agents-md.md +110 -0
  231. package/docs/troubleshooting.md +2 -2
  232. package/package.json +1 -1
  233. package/skills/create-agent/SKILL.md +2 -1
  234. package/skills/mcp-auth/SKILL.md +6 -3
  235. package/skills/setup-slack/SKILL.md +23 -13
  236. package/src/bin/agent-serve.ts +13 -1
  237. package/src/channels/origin/api.ts +24 -5
  238. package/src/channels/origin/binding.ts +55 -26
  239. package/src/channels/origin/index.ts +1 -0
  240. package/src/channels/origin/origin-channel.ts +4 -0
  241. package/src/channels/origin/reactions.ts +82 -0
  242. package/src/channels/origin/types.ts +11 -0
  243. package/src/channels/slack/approvals.ts +105 -0
  244. package/src/channels/slack/cli-project.ts +267 -0
  245. package/src/channels/slack/defaults.ts +109 -46
  246. package/src/channels/slack/init.ts +10 -5
  247. package/src/channels/slack/interactive.ts +35 -9
  248. package/src/channels/slack/manifest.ts +1 -1
  249. package/src/channels/slack/setup.ts +10 -6
  250. package/src/channels/slack/slack-channel.ts +1 -0
  251. package/src/channels/slack/tool-approval-events.ts +93 -0
  252. package/src/evals/run-facts.ts +32 -0
  253. package/src/internal/advertise-tools.ts +31 -10
  254. package/src/internal/approval-gate.ts +20 -29
  255. package/src/internal/chat-client.ts +19 -0
  256. package/src/internal/cli-deploy.ts +79 -9
  257. package/src/internal/cli-mcp-oauth.ts +7 -21
  258. package/src/internal/cli-slack-manual.ts +350 -18
  259. package/src/internal/cli-slack.ts +40 -84
  260. package/src/internal/cursor/account-mcp.ts +21 -0
  261. package/src/internal/cursor-agent-template.ts +1 -0
  262. package/src/internal/deploy-client.ts +43 -0
  263. package/src/internal/env-file.ts +84 -0
  264. package/src/internal/framework-file-storage.ts +7 -1
  265. package/src/internal/hosted-delivery-protocol.ts +7 -0
  266. package/src/internal/hosted-delivery.ts +100 -27
  267. package/src/internal/hosted-execution-diag.ts +207 -0
  268. package/src/internal/http-channel.ts +99 -0
  269. package/src/internal/init-project.ts +2 -0
  270. package/src/internal/logs-client.ts +5 -0
  271. package/src/internal/mcp-endpoint.ts +13 -4
  272. package/src/internal/mcp-host.ts +77 -11
  273. package/src/internal/mcp-oauth-on-demand.ts +352 -0
  274. package/src/internal/mcp-oauth-persist.ts +127 -0
  275. package/src/internal/mcp-oauth.ts +17 -1
  276. package/src/internal/otel/collector.ts +30 -0
  277. package/src/internal/server.ts +59 -9
  278. package/src/internal/session-engine.ts +659 -56
  279. package/src/internal/sessions-client.ts +17 -8
  280. package/src/internal/storage-coordinator.ts +38 -8
  281. package/src/internal/storage-roles.ts +7 -0
  282. package/src/internal/stream-progress.ts +19 -0
  283. package/src/internal/tool-policy.ts +18 -0
  284. package/src/internal/trajectory.ts +10 -0
  285. package/src/internal/turn-outbox.ts +85 -0
  286. package/src/storage-backends/cursor-hosted.ts +13 -1
  287. package/src/storage.ts +12 -0
  288. package/src/types.ts +68 -8
  289. package/templates/agents-md/README.md +39 -0
  290. package/templates/agents-md/agent/agent.ts +8 -0
  291. package/templates/agents-md/agent/channels/github.ts +10 -0
  292. package/templates/agents-md/agent/channels/queue.ts +28 -0
  293. package/templates/agents-md/agent/channels/slack.ts +9 -0
  294. package/templates/agents-md/agent/instructions.md +39 -0
  295. package/templates/agents-md/agent/lib/collect.ts +439 -0
  296. package/templates/agents-md/agent/lib/config.ts +8 -0
  297. package/templates/agents-md/agent/lib/paths.ts +51 -0
  298. package/templates/agents-md/agent/schedules/weekly.ts +17 -0
  299. package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
  300. package/templates/agents-md/evals/agents.eval.ts +135 -0
  301. package/templates/agents-md/evals/evals.config.ts +5 -0
  302. package/templates/agents-md/init.json +38 -0
  303. package/templates/agents-md/package.json +18 -0
  304. package/templates/agents-md/tsconfig.json +12 -0
  305. package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +0 -1
  306. package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
  307. package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
  308. package/dist/docs/assets/index.md.CI93Ltlb.js +0 -5
  309. package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
  310. package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
  311. package/dist/docs/design/agsh.md +0 -406
  312. package/dist/playground/assets/index-B1DDvBeC.css +0 -1
  313. package/docs/design/agsh.md +0 -406
  314. /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
  315. /package/dist/docs/assets/{index.md.CI93Ltlb.lean.js → index.md.CVeRUOeZ.lean.js} +0 -0
  316. /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
  317. /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
  318. /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.5zOAbV86.lean.js} +0 -0
  319. /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
  320. /package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.B84gw9Ii.lean.js} +0 -0
  321. /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
@@ -6,6 +6,8 @@ import type { ApprovalDecision } from "../../types.js";
6
6
 
7
7
  export const SLACK_APPROVE_ACTION_ID = "agent_serve_approve";
8
8
  export const SLACK_DENY_ACTION_ID = "agent_serve_deny";
9
+ export const SLACK_AUTH_CONNECTED_ACTION_ID = "agent_serve_auth_connected";
10
+ export const SLACK_AUTH_CANCEL_ACTION_ID = "agent_serve_auth_cancel";
9
11
 
10
12
  export interface SlackApprovalActionValue {
11
13
  sessionId: string;
@@ -54,6 +56,29 @@ export function decisionFromActionId(
54
56
  return null;
55
57
  }
56
58
 
59
+ export function decisionFromAuthActionId(
60
+ actionId: string
61
+ ): ApprovalDecision | null {
62
+ if (actionId === SLACK_AUTH_CONNECTED_ACTION_ID) {
63
+ return "approve";
64
+ }
65
+ if (actionId === SLACK_AUTH_CANCEL_ACTION_ID) {
66
+ return "deny";
67
+ }
68
+ return null;
69
+ }
70
+
71
+ export function authThreadStatus(connection: string): string {
72
+ const prefix = "Waiting for ";
73
+ const suffix = " auth…";
74
+ const budget = 50 - prefix.length - suffix.length;
75
+ const name =
76
+ connection.length <= budget
77
+ ? connection
78
+ : `${connection.slice(0, Math.max(0, budget - 1))}…`;
79
+ return `${prefix}${name}${suffix}`;
80
+ }
81
+
57
82
  const REDACTED = "[REDACTED]";
58
83
  const SENSITIVE_KEY =
59
84
  /(?:pass(?:word|wd)?|secret|token|api[_-]?key|authorization|credential|private[_-]?key|access[_-]?key)/i;
@@ -176,6 +201,86 @@ export function buildApprovalBlocks(args: {
176
201
  ];
177
202
  }
178
203
 
204
+ export function buildAuthBlocks(args: {
205
+ sessionId: string;
206
+ callId: string;
207
+ toolName: string;
208
+ connection: string;
209
+ authUrl: string;
210
+ }): unknown[] {
211
+ const value = encodeApprovalActionValue({
212
+ sessionId: args.sessionId,
213
+ callId: args.callId,
214
+ });
215
+ const label =
216
+ args.connection.length > 20
217
+ ? `Authorize ${args.connection.slice(0, 17)}…`
218
+ : `Authorize ${args.connection}`;
219
+ return [
220
+ {
221
+ type: "header",
222
+ text: {
223
+ type: "plain_text",
224
+ text: "MCP authorization needed",
225
+ emoji: true,
226
+ },
227
+ },
228
+ {
229
+ type: "section",
230
+ text: {
231
+ type: "mrkdwn",
232
+ text: `Connect \`${args.connection}\` so \`${args.toolName}\` can continue.`,
233
+ },
234
+ },
235
+ {
236
+ type: "actions",
237
+ block_id: `auth_${args.callId}`.slice(0, 255),
238
+ elements: [
239
+ {
240
+ type: "button",
241
+ text: { type: "plain_text", text: label, emoji: true },
242
+ url: args.authUrl,
243
+ },
244
+ {
245
+ type: "button",
246
+ action_id: SLACK_AUTH_CONNECTED_ACTION_ID,
247
+ text: { type: "plain_text", text: "Connected", emoji: true },
248
+ style: "primary",
249
+ value,
250
+ },
251
+ {
252
+ type: "button",
253
+ action_id: SLACK_AUTH_CANCEL_ACTION_ID,
254
+ text: { type: "plain_text", text: "Cancel", emoji: true },
255
+ style: "danger",
256
+ value,
257
+ },
258
+ ],
259
+ },
260
+ ];
261
+ }
262
+
263
+ export function buildResolvedAuthBlocks(args: {
264
+ connection: string;
265
+ decision: ApprovalDecision;
266
+ byUserId?: string;
267
+ }): unknown[] {
268
+ const verb = args.decision === "approve" ? "Connected" : "Cancelled";
269
+ const by =
270
+ args.byUserId === undefined || args.byUserId === ""
271
+ ? ""
272
+ : ` by <@${args.byUserId}>`;
273
+ return [
274
+ {
275
+ type: "section",
276
+ text: {
277
+ type: "mrkdwn",
278
+ text: `*${verb}*${by} — \`${args.connection}\``,
279
+ },
280
+ },
281
+ ];
282
+ }
283
+
179
284
  export function buildResolvedApprovalBlocks(args: {
180
285
  toolName: string;
181
286
  decision: ApprovalDecision;
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Official Slack CLI project files for `slack init --manual`.
3
+ *
4
+ * `.slack/` is the committed Slack CLI project: hook, manifests, hooks.json.
5
+ * Agent SDK serve owns the socket; do not `slack deploy` / `slack run`.
6
+ */
7
+
8
+ import { randomUUID } from "node:crypto";
9
+ import { existsSync } from "node:fs";
10
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
11
+ import { homedir } from "node:os";
12
+ import { join } from "node:path";
13
+
14
+ export const SLACK_CLI_HOME_BIN = ".slack/bin/slack";
15
+
16
+ export const SLACK_CLI_GET_MANIFEST = "node .slack/get-manifest.mjs";
17
+
18
+ export type SlackCliEnvironment = "local" | "deployed";
19
+
20
+ const TEAM_ID_RE = /\(Team ID: ([TE][A-Z0-9]+)\)/g;
21
+
22
+ const GET_MANIFEST_JS = `import { readFileSync } from "node:fs";
23
+ import { dirname, join } from "node:path";
24
+ import { fileURLToPath } from "node:url";
25
+
26
+ const here = dirname(fileURLToPath(import.meta.url));
27
+
28
+ function target() {
29
+ const env = process.env.SLACK_ENV ?? process.env.SLACK_APP_ENVIRONMENT;
30
+ if (env === "deployed" || env === "prod") return "manifest.prod.json";
31
+ if (env === "local" || env === "dev") return "manifest.dev.json";
32
+ try {
33
+ const raw = readFileSync(join(here, "cli-environment"), "utf8").trim();
34
+ if (raw === "deployed" || raw === "prod") return "manifest.prod.json";
35
+ } catch {
36
+ // default local
37
+ }
38
+ return "manifest.dev.json";
39
+ }
40
+
41
+ process.stdout.write(readFileSync(join(here, target()), "utf8"));
42
+ `;
43
+
44
+ function isEnoent(error: unknown): boolean {
45
+ return (
46
+ error instanceof Error &&
47
+ "code" in error &&
48
+ (error as NodeJS.ErrnoException).code === "ENOENT"
49
+ );
50
+ }
51
+
52
+ /** Official Slack CLI, if present. `SLACK_CLI` overrides. */
53
+ export function detectSlackCli(
54
+ env: NodeJS.ProcessEnv = process.env
55
+ ): string | undefined {
56
+ const override = env.SLACK_CLI;
57
+ if (override !== undefined && override !== "") {
58
+ return override;
59
+ }
60
+ const home = env.HOME ?? env.USERPROFILE ?? homedir();
61
+ const homeBin = join(home, SLACK_CLI_HOME_BIN);
62
+ return existsSync(homeBin) ? homeBin : undefined;
63
+ }
64
+
65
+ /** Hook + manifests under `.slack/`. Merge hooks.json; keep existing config. */
66
+ export async function writeSlackCliProject(
67
+ rootDir: string,
68
+ manifests: ReadonlyArray<{ name: string; contents: string }>
69
+ ): Promise<string[]> {
70
+ const slackDir = join(rootDir, ".slack");
71
+ await mkdir(slackDir, { recursive: true });
72
+ const created: string[] = [];
73
+
74
+ await writeFile(join(slackDir, "get-manifest.mjs"), GET_MANIFEST_JS);
75
+ created.push(".slack/get-manifest.mjs");
76
+
77
+ for (const file of manifests) {
78
+ await writeFile(join(slackDir, file.name), file.contents);
79
+ created.push(`.slack/${file.name}`);
80
+ }
81
+
82
+ if (await upsertGetManifestHook(slackDir)) {
83
+ created.push(".slack/hooks.json");
84
+ }
85
+ if (
86
+ await writeIfMissing(
87
+ join(slackDir, "config.json"),
88
+ `${JSON.stringify(
89
+ {
90
+ manifest: { source: "local" },
91
+ project_id: randomUUID(),
92
+ },
93
+ null,
94
+ 2
95
+ )}\n`
96
+ )
97
+ ) {
98
+ created.push(".slack/config.json");
99
+ }
100
+ if (await mergeGitignore(join(slackDir, ".gitignore"))) {
101
+ created.push(".slack/.gitignore");
102
+ }
103
+ return created;
104
+ }
105
+
106
+ async function upsertGetManifestHook(slackDir: string): Promise<boolean> {
107
+ const path = join(slackDir, "hooks.json");
108
+ let existing: { hooks?: Record<string, string> };
109
+ try {
110
+ const parsed: unknown = JSON.parse(await readFile(path, "utf8"));
111
+ if (
112
+ parsed === null ||
113
+ typeof parsed !== "object" ||
114
+ Array.isArray(parsed)
115
+ ) {
116
+ throw new Error(".slack/hooks.json is not a JSON object");
117
+ }
118
+ existing = parsed as { hooks?: Record<string, string> };
119
+ } catch (error) {
120
+ if (!isEnoent(error)) {
121
+ throw error;
122
+ }
123
+ await writeFile(
124
+ path,
125
+ `${JSON.stringify({ hooks: { "get-manifest": SLACK_CLI_GET_MANIFEST } }, null, 2)}\n`
126
+ );
127
+ return true;
128
+ }
129
+ const hooks = existing.hooks ?? {};
130
+ if (hooks["get-manifest"] === SLACK_CLI_GET_MANIFEST) {
131
+ return false;
132
+ }
133
+ hooks["get-manifest"] = SLACK_CLI_GET_MANIFEST;
134
+ existing.hooks = hooks;
135
+ await writeFile(path, `${JSON.stringify(existing, null, 2)}\n`);
136
+ return true;
137
+ }
138
+
139
+ async function writeIfMissing(
140
+ path: string,
141
+ contents: string
142
+ ): Promise<boolean> {
143
+ try {
144
+ await writeFile(path, contents, { flag: "wx" });
145
+ return true;
146
+ } catch (error) {
147
+ if (
148
+ error instanceof Error &&
149
+ "code" in error &&
150
+ (error as NodeJS.ErrnoException).code === "EEXIST"
151
+ ) {
152
+ return false;
153
+ }
154
+ throw error;
155
+ }
156
+ }
157
+
158
+ async function mergeGitignore(path: string): Promise<boolean> {
159
+ const lines = ["apps.dev.json", "cache/", "cli-environment"];
160
+ let existing = "";
161
+ try {
162
+ existing = await readFile(path, "utf8");
163
+ } catch (error) {
164
+ if (!isEnoent(error)) {
165
+ throw error;
166
+ }
167
+ await writeFile(path, `${lines.join("\n")}\n`);
168
+ return true;
169
+ }
170
+ const have = new Set(
171
+ existing
172
+ .split(/\r?\n/)
173
+ .map((line) => line.trim())
174
+ .filter((line) => line !== "")
175
+ );
176
+ const missing = lines.filter((line) => !have.has(line));
177
+ if (missing.length === 0) {
178
+ return false;
179
+ }
180
+ const prefix =
181
+ existing === "" || existing.endsWith("\n") ? existing : `${existing}\n`;
182
+ await writeFile(path, `${prefix}${missing.join("\n")}\n`);
183
+ return true;
184
+ }
185
+
186
+ export function parseSlackAuthTeams(text: string): string[] {
187
+ const ids: string[] = [];
188
+ for (const match of text.matchAll(TEAM_ID_RE)) {
189
+ const id = match[1];
190
+ if (id !== undefined && !ids.includes(id)) {
191
+ ids.push(id);
192
+ }
193
+ }
194
+ return ids;
195
+ }
196
+
197
+ export async function readSlackCliAppId(
198
+ dir: string,
199
+ environment: SlackCliEnvironment,
200
+ team?: string
201
+ ): Promise<string | undefined> {
202
+ const apps = await loadAppMap(dir, environment);
203
+ if (apps === undefined) {
204
+ return undefined;
205
+ }
206
+ if (team !== undefined) {
207
+ return appIdOf(apps[team]);
208
+ }
209
+ return firstAppId(apps);
210
+ }
211
+
212
+ async function loadAppMap(
213
+ dir: string,
214
+ environment: SlackCliEnvironment
215
+ ): Promise<Record<string, unknown> | undefined> {
216
+ if (environment === "local") {
217
+ return readJson(join(dir, ".slack/apps.dev.json"));
218
+ }
219
+ const deployed = await readJson(join(dir, ".slack/apps.json"));
220
+ if (deployed === undefined) {
221
+ return undefined;
222
+ }
223
+ const apps = deployed.apps;
224
+ if (apps !== undefined && typeof apps === "object" && apps !== null) {
225
+ return apps as Record<string, unknown>;
226
+ }
227
+ return deployed;
228
+ }
229
+
230
+ function firstAppId(value: Record<string, unknown>): string | undefined {
231
+ for (const entry of Object.values(value)) {
232
+ const id = appIdOf(entry);
233
+ if (id !== undefined) {
234
+ return id;
235
+ }
236
+ }
237
+ return undefined;
238
+ }
239
+
240
+ function appIdOf(entry: unknown): string | undefined {
241
+ if (entry === undefined || entry === null || typeof entry !== "object") {
242
+ return undefined;
243
+ }
244
+ const appId = (entry as { app_id?: unknown }).app_id;
245
+ if (typeof appId === "string" && appId.startsWith("A")) {
246
+ return appId;
247
+ }
248
+ return undefined;
249
+ }
250
+
251
+ async function readJson(
252
+ path: string
253
+ ): Promise<Record<string, unknown> | undefined> {
254
+ try {
255
+ const parsed: unknown = JSON.parse(await readFile(path, "utf8"));
256
+ if (
257
+ parsed === null ||
258
+ typeof parsed !== "object" ||
259
+ Array.isArray(parsed)
260
+ ) {
261
+ return undefined;
262
+ }
263
+ return parsed as Record<string, unknown>;
264
+ } catch {
265
+ return undefined;
266
+ }
267
+ }
@@ -2,7 +2,7 @@
2
2
  * Default inbound handlers and stream-event sinks for the Slack channel.
3
3
  */
4
4
 
5
- import type { ChannelEventHandlers } from "../../types.js";
5
+ import type { ChannelEventContext, ChannelEventHandlers } from "../../types.js";
6
6
  import {
7
7
  appendChatStream,
8
8
  postMessage,
@@ -174,6 +174,91 @@ export function buildDefaultEvents(
174
174
  }
175
175
  };
176
176
 
177
+ /**
178
+ * Deliver assistant text to the bound Slack thread. Used by a stop-reason
179
+ * `message.completed` and by `turn.completed` when the last assistant
180
+ * block was `finishReason: "tool_call"` (answer then `tag_artifact`,
181
+ * etc.) so the playground result still reaches Slack.
182
+ */
183
+ const deliverReplyText = async (args: {
184
+ channel: ChannelEventContext<SlackChannelState>;
185
+ text: string;
186
+ sessionId: string;
187
+ }): Promise<boolean> => {
188
+ const log = getLog();
189
+ const state = args.channel.state;
190
+ if (
191
+ state.channelId === null ||
192
+ state.threadTs === null ||
193
+ args.text === ""
194
+ ) {
195
+ return false;
196
+ }
197
+ if (state.lastPostedText === args.text) {
198
+ // Stream append sets this watermark while chat.stopStream is still
199
+ // pending. Treat a match as "already on Slack", not "already finalized".
200
+ const streamTs = state.streamTs;
201
+ if (streamTs !== null && streamTs !== "") {
202
+ const canStream = streaming && state.streamingUnavailable !== true;
203
+ if (canStream) {
204
+ await strategy.finalize({
205
+ channel: args.channel,
206
+ channelId: state.streamChannelId ?? state.channelId,
207
+ text: args.text,
208
+ sessionId: args.sessionId,
209
+ });
210
+ } else {
211
+ await strategy.cleanupOrphan({ channel: args.channel });
212
+ }
213
+ }
214
+ return true;
215
+ }
216
+
217
+ const canStream = streaming && state.streamingUnavailable !== true;
218
+ if (canStream) {
219
+ const { delivered } = await strategy.finalize({
220
+ channel: args.channel,
221
+ channelId: state.streamChannelId ?? state.channelId,
222
+ text: args.text,
223
+ sessionId: args.sessionId,
224
+ });
225
+ if (delivered) {
226
+ return true;
227
+ }
228
+ }
229
+
230
+ try {
231
+ const posted = await postMessage({
232
+ botToken,
233
+ channel: state.channelId,
234
+ text: args.text,
235
+ threadTs: state.threadTs,
236
+ });
237
+ state.lastPostedText = args.text;
238
+ state.lastBotMessageTs = posted.ts;
239
+ log.info("reply delivered via postMessage", {
240
+ channel: state.channelId,
241
+ thread_ts: state.threadTs,
242
+ message_ts: posted.ts,
243
+ session: args.sessionId,
244
+ chars: args.text.length,
245
+ });
246
+ return true;
247
+ } catch (error) {
248
+ log.error(
249
+ `postMessage failed: ${
250
+ error instanceof Error ? error.message : String(error)
251
+ }`,
252
+ {
253
+ channel: state.channelId,
254
+ thread_ts: state.threadTs,
255
+ session: args.sessionId,
256
+ }
257
+ );
258
+ return false;
259
+ }
260
+ };
261
+
177
262
  return {
178
263
  async "turn.started"(event, channel) {
179
264
  const log = getLog();
@@ -351,7 +436,6 @@ export function buildDefaultEvents(
351
436
  },
352
437
 
353
438
  async "message.completed"(event, channel) {
354
- const log = getLog();
355
439
  if (event.data.finishReason === "tool_call") {
356
440
  channel.state.pendingToolCallMessage =
357
441
  firstNonEmptyLine(event.data.text) ?? null;
@@ -372,50 +456,11 @@ export function buildDefaultEvents(
372
456
  return;
373
457
  }
374
458
 
375
- const canStream = streaming && state.streamingUnavailable !== true;
376
-
377
- if (canStream) {
378
- const { delivered } = await strategy.finalize({
379
- channel,
380
- channelId: state.streamChannelId ?? state.channelId,
381
- text: event.data.text,
382
- sessionId: event.sessionId,
383
- });
384
- if (delivered) {
385
- await clearWorkingStatus(state.channelId, state.threadTs);
386
- return;
387
- }
388
- }
389
-
390
- try {
391
- const posted = await postMessage({
392
- botToken,
393
- channel: state.channelId,
394
- text: event.data.text,
395
- threadTs: state.threadTs,
396
- });
397
- channel.state.lastPostedText = event.data.text;
398
- channel.state.lastBotMessageTs = posted.ts;
399
- log.info("reply delivered via postMessage", {
400
- channel: state.channelId,
401
- thread_ts: state.threadTs,
402
- message_ts: posted.ts,
403
- session: event.sessionId,
404
- chars: event.data.text.length,
405
- });
406
- } catch (error) {
407
- log.error(
408
- `postMessage failed: ${
409
- error instanceof Error ? error.message : String(error)
410
- }`,
411
- {
412
- channel: state.channelId,
413
- thread_ts: state.threadTs,
414
- session: event.sessionId,
415
- }
416
- );
417
- }
418
-
459
+ await deliverReplyText({
460
+ channel,
461
+ text: event.data.text,
462
+ sessionId: event.sessionId,
463
+ });
419
464
  await clearWorkingStatus(state.channelId, state.threadTs);
420
465
  },
421
466
 
@@ -428,6 +473,24 @@ export function buildDefaultEvents(
428
473
  thread_ts: channel.state.threadTs,
429
474
  });
430
475
 
476
+ // Mid-turn `tool_call` text is held as status, not posted. If the
477
+ // turn ends without a later `finishReason: "stop"` block, the
478
+ // playground still has `result` (last assistant text) and Slack
479
+ // would otherwise stay silent.
480
+ const result = event.data.result;
481
+ const unpublished =
482
+ typeof result === "string" &&
483
+ result !== "" &&
484
+ result !== channel.state.lastPostedText;
485
+ if (unpublished) {
486
+ await deliverReplyText({
487
+ channel,
488
+ text: result,
489
+ sessionId: event.sessionId,
490
+ });
491
+ }
492
+ // Always close leftovers. deliverReplyText may fall through to
493
+ // postMessage when streaming is unavailable and leave streamTs open.
431
494
  await strategy.cleanupOrphan({ channel });
432
495
 
433
496
  // Always drop the assistant chip. message.completed already clears
@@ -2,13 +2,14 @@
2
2
  * Scaffold a Slack channel into an agent-serve project.
3
3
  *
4
4
  * `initSlackChannel` is the create-safe writer: `agent/channels/slack.ts`
5
- * only. DIY manifests for paste at api.slack.com live in
5
+ * only. DIY manifests + Slack CLI `.slack/` live in
6
6
  * {@link initSlackManual} so `slack create` cannot reach them.
7
7
  */
8
8
 
9
9
  import { mkdir, writeFile } from "node:fs/promises";
10
10
  import { basename, join, resolve } from "node:path";
11
11
  import { PACKAGE_NAME } from "../../internal/distribution.js";
12
+ import { writeSlackCliProject } from "./cli-project.js";
12
13
  import { envPrefixFromName, slackEnvKeys } from "./credentials.js";
13
14
  import {
14
15
  buildSlackManifestPair,
@@ -131,7 +132,8 @@ export async function initSlackChannel(
131
132
 
132
133
  /**
133
134
  * `slack init --manual`: channel file plus DIY manifests
134
- * (`managedByCursor: false`), env.example, and setup-status.json.
135
+ * (`managedByCursor: false`), Slack CLI `.slack/` project, env.example,
136
+ * and setup-status.json.
135
137
  */
136
138
  export async function initSlackManual(
137
139
  options: InitSlackManualOptions
@@ -151,9 +153,6 @@ export async function initSlackManual(
151
153
  description: options.description,
152
154
  channelPosts: options.channelPosts,
153
155
  });
154
- for (const file of slackManifestFileEntries(pair)) {
155
- await write(`.agent-serve/slack/${file.name}`, file.contents);
156
- }
157
156
  await write(
158
157
  ".agent-serve/slack/env.example",
159
158
  `# Copy to .env.local (or export in your shell). Use the *dev* Slack app
@@ -176,6 +175,12 @@ ${target.envKeys.app}=xapp-...
176
175
  2
177
176
  )}\n`
178
177
  );
178
+ files.push(
179
+ ...(await writeSlackCliProject(
180
+ target.rootDir,
181
+ slackManifestFileEntries(pair)
182
+ ))
183
+ );
179
184
 
180
185
  return {
181
186
  rootDir: target.rootDir,
@@ -15,10 +15,15 @@ import type {
15
15
  ChannelSession,
16
16
  HostContext,
17
17
  PendingApproval,
18
+ PendingAuth,
18
19
  SendMessageFn,
19
20
  } from "../../types.js";
20
21
  import { updateMessage } from "./api.js";
21
- import { decisionFromActionId, parseApprovalActionValue } from "./approvals.js";
22
+ import {
23
+ decisionFromActionId,
24
+ decisionFromAuthActionId,
25
+ parseApprovalActionValue,
26
+ } from "./approvals.js";
22
27
  import { buildSlackInteractiveApprovalAuth } from "./auth.js";
23
28
  import { dispatchInboundMessage } from "./dispatch.js";
24
29
  import { isExternalSlackSender } from "./external-policy.js";
@@ -41,6 +46,12 @@ export interface SlackInteractiveDispatchDeps {
41
46
  decision: ApprovalDecision,
42
47
  auth: AuthContext | null
43
48
  ) => Promise<PendingApproval>;
49
+ resolveAuth: (
50
+ sessionId: string,
51
+ callId: string,
52
+ decision: ApprovalDecision,
53
+ auth: AuthContext | null
54
+ ) => Promise<PendingAuth>;
44
55
  waitUntil: (promise: Promise<unknown>) => void;
45
56
  log?: SlackLog;
46
57
  /** Defaults to true. */
@@ -61,11 +72,17 @@ export async function dispatchInteractiveFromPayload(
61
72
  return false;
62
73
  }
63
74
 
64
- const decision = decisionFromActionId(parsed.actionId);
75
+ const approvalDecision = decisionFromActionId(parsed.actionId);
76
+ const authDecision = decisionFromAuthActionId(parsed.actionId);
65
77
  const value = parseApprovalActionValue(parsed.value);
66
- if (decision === null || value === null) {
78
+ if (value === null || (approvalDecision === null && authDecision === null)) {
67
79
  return false;
68
80
  }
81
+ const decision = approvalDecision ?? authDecision;
82
+ if (decision === null) {
83
+ return false;
84
+ }
85
+ const kind = approvalDecision !== null ? "approval" : "auth";
69
86
 
70
87
  log.info("interactive approval", {
71
88
  decision,
@@ -153,12 +170,21 @@ export async function dispatchInteractiveFromPayload(
153
170
  });
154
171
 
155
172
  try {
156
- await deps.resolveApproval(
157
- value.sessionId,
158
- value.callId,
159
- decision,
160
- clickingAuth
161
- );
173
+ if (kind === "auth") {
174
+ await deps.resolveAuth(
175
+ value.sessionId,
176
+ value.callId,
177
+ decision,
178
+ clickingAuth
179
+ );
180
+ } else {
181
+ await deps.resolveApproval(
182
+ value.sessionId,
183
+ value.callId,
184
+ decision,
185
+ clickingAuth
186
+ );
187
+ }
162
188
  } catch (error) {
163
189
  log.warn("interactive approval resolve failed", {
164
190
  session: value.sessionId,