@cursor/july 0.1.39 → 0.1.41

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 (259) hide show
  1. package/AGENTS.md +4 -3
  2. package/dist/bin/agent-serve.js +28 -9
  3. package/dist/channels/slack/api.d.ts.map +1 -1
  4. package/dist/channels/slack/api.js +18 -8
  5. package/dist/channels/slack/attachments.d.ts +82 -0
  6. package/dist/channels/slack/attachments.d.ts.map +1 -0
  7. package/dist/channels/slack/attachments.js +520 -0
  8. package/dist/channels/slack/cursor-account.d.ts +7 -1
  9. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  10. package/dist/channels/slack/cursor-account.js +2 -2
  11. package/dist/channels/slack/defaults.d.ts.map +1 -1
  12. package/dist/channels/slack/defaults.js +26 -17
  13. package/dist/channels/slack/dispatch.d.ts +4 -3
  14. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  15. package/dist/channels/slack/dispatch.js +84 -21
  16. package/dist/channels/slack/inbound.d.ts +6 -0
  17. package/dist/channels/slack/inbound.d.ts.map +1 -1
  18. package/dist/channels/slack/inbound.js +48 -2
  19. package/dist/channels/slack/index.d.ts +1 -0
  20. package/dist/channels/slack/index.d.ts.map +1 -1
  21. package/dist/channels/slack/index.js +1 -0
  22. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  23. package/dist/channels/slack/live-delivery.js +16 -4
  24. package/dist/channels/slack/manifest.d.ts.map +1 -1
  25. package/dist/channels/slack/manifest.js +1 -0
  26. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  27. package/dist/channels/slack/slack-channel.js +7 -4
  28. package/dist/channels/slack/types.d.ts +49 -1
  29. package/dist/channels/slack/types.d.ts.map +1 -1
  30. package/dist/docs/404.html +2 -2
  31. package/dist/docs/ab.html +6 -6
  32. package/dist/docs/assets/{ab.md.6cLOW7--.js → ab.md.hdxsvX5M.js} +2 -2
  33. package/dist/docs/assets/{app.BQ8Hihdf.js → app.mIw24pb1.js} +1 -1
  34. package/dist/docs/assets/{building-with-agents.md.txrcGU2B.js → building-with-agents.md.PeZaZA1P.js} +1 -1
  35. package/dist/docs/assets/chunks/@localSearchIndexroot.BA7V8dEJ.js +1 -0
  36. package/dist/docs/assets/chunks/{VPLocalSearchBox.BBCr8Yuy.js → VPLocalSearchBox.hOzpPwrG.js} +1 -1
  37. package/dist/docs/assets/chunks/{theme.CK_NiGC-.js → theme.BtFhUYmh.js} +2 -2
  38. package/dist/docs/assets/{deployment.md.CuK5SNjN.js → deployment.md.BHVjVqRa.js} +4 -4
  39. package/dist/docs/assets/{deployment.md.CuK5SNjN.lean.js → deployment.md.BHVjVqRa.lean.js} +1 -1
  40. package/dist/docs/assets/{evals.md.BQXI3rXy.js → evals.md.qeoGgPw0.js} +3 -3
  41. package/dist/docs/assets/example-agents_index.md.xmJ87d_3.js +2 -0
  42. package/dist/docs/assets/{guides_webhooks.md.BERuBSJW.js → guides_webhooks.md.DB-r_er9.js} +47 -4
  43. package/dist/docs/assets/{guides_webhooks.md.BERuBSJW.lean.js → guides_webhooks.md.DB-r_er9.lean.js} +1 -1
  44. package/dist/docs/assets/index.md.BKpbkCa6.js +24 -0
  45. package/dist/docs/assets/{index.md.CmhptOmN.lean.js → index.md.BKpbkCa6.lean.js} +1 -1
  46. package/dist/docs/assets/{quickstart.md.C_b6ESpD.js → quickstart.md.rIbfahJM.js} +4 -4
  47. package/dist/docs/assets/{reference_cli.md.Byvrg8eu.js → reference_cli.md.DqEqQpGn.js} +5 -5
  48. package/dist/docs/assets/{reference_cli.md.Byvrg8eu.lean.js → reference_cli.md.DqEqQpGn.lean.js} +1 -1
  49. package/dist/docs/assets/{reference_hooks.md.BGDw4VLm.js → reference_hooks.md.BHeG-vjK.js} +2 -2
  50. package/dist/docs/assets/{reference_hooks.md.BGDw4VLm.lean.js → reference_hooks.md.BHeG-vjK.lean.js} +1 -1
  51. package/dist/docs/assets/{storage.md.mQDtIULc.js → storage.md.DcGSnIxX.js} +10 -4
  52. package/dist/docs/assets/{storage.md.mQDtIULc.lean.js → storage.md.DcGSnIxX.lean.js} +1 -1
  53. package/dist/docs/assets/troubleshooting.md.CkgwmVI0.js +1 -0
  54. package/dist/docs/assets/{troubleshooting.md.B5RVX_tL.lean.js → troubleshooting.md.CkgwmVI0.lean.js} +1 -1
  55. package/dist/docs/building-with-agents.html +5 -5
  56. package/dist/docs/concepts.html +3 -3
  57. package/dist/docs/deployment.html +6 -6
  58. package/dist/docs/evals.html +7 -7
  59. package/dist/docs/example-agents/approval-buddy.html +3 -3
  60. package/dist/docs/example-agents/benny.html +3 -3
  61. package/dist/docs/example-agents/bugbot.html +3 -3
  62. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  63. package/dist/docs/example-agents/codeowners-review.html +3 -3
  64. package/dist/docs/example-agents/concierge.html +3 -3
  65. package/dist/docs/example-agents/fsd.html +3 -3
  66. package/dist/docs/example-agents/index.html +5 -5
  67. package/dist/docs/example-agents/knowledge-base.html +3 -3
  68. package/dist/docs/example-agents/oncall.html +3 -3
  69. package/dist/docs/example-agents/security-reviewer.html +3 -3
  70. package/dist/docs/example-agents/slack-agent.html +3 -3
  71. package/dist/docs/example-agents/weather-agent.html +3 -3
  72. package/dist/docs/guides/agent-to-agent.html +3 -3
  73. package/dist/docs/guides/cloud-runtime.html +3 -3
  74. package/dist/docs/guides/github.html +3 -3
  75. package/dist/docs/guides/human-in-the-loop.html +3 -3
  76. package/dist/docs/guides/mcp-oauth.html +3 -3
  77. package/dist/docs/guides/slack.html +3 -3
  78. package/dist/docs/guides/webhooks.html +49 -6
  79. package/dist/docs/hashmap.json +1 -1
  80. package/dist/docs/hillclimbing.html +3 -3
  81. package/dist/docs/index.html +7 -7
  82. package/dist/docs/quickstart.html +8 -8
  83. package/dist/docs/reference/agent-config.html +3 -3
  84. package/dist/docs/reference/artifacts.html +3 -3
  85. package/dist/docs/reference/channels.html +3 -3
  86. package/dist/docs/reference/cli.html +8 -8
  87. package/dist/docs/reference/connections.html +3 -3
  88. package/dist/docs/reference/hooks.html +5 -5
  89. package/dist/docs/reference/http-api.html +3 -3
  90. package/dist/docs/reference/instructions.html +3 -3
  91. package/dist/docs/reference/playground.html +3 -3
  92. package/dist/docs/reference/project-layout.html +3 -3
  93. package/dist/docs/reference/prompt.html +3 -3
  94. package/dist/docs/reference/schedules.html +3 -3
  95. package/dist/docs/reference/sessions.html +3 -3
  96. package/dist/docs/reference/skills.html +3 -3
  97. package/dist/docs/reference/subagents.html +3 -3
  98. package/dist/docs/reference/tools.html +3 -3
  99. package/dist/docs/scaffolding-agents.html +3 -3
  100. package/dist/docs/storage.html +14 -8
  101. package/dist/docs/troubleshooting.html +5 -5
  102. package/dist/index.d.ts +4 -2
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +3 -1
  105. package/dist/internal/cli-ax.d.ts.map +1 -1
  106. package/dist/internal/cli-ax.js +22 -2
  107. package/dist/internal/cli-deploy.d.ts +6 -0
  108. package/dist/internal/cli-deploy.d.ts.map +1 -1
  109. package/dist/internal/cli-deploy.js +14 -4
  110. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  111. package/dist/internal/cli-mcp-oauth.js +3 -1
  112. package/dist/internal/cli-mcp.js +4 -4
  113. package/dist/internal/cursor-event-relay.d.ts +45 -0
  114. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  115. package/dist/internal/cursor-event-relay.js +96 -2
  116. package/dist/internal/deploy-client.js +1 -1
  117. package/dist/internal/discovery.d.ts.map +1 -1
  118. package/dist/internal/discovery.js +35 -7
  119. package/dist/internal/distribution.d.ts.map +1 -1
  120. package/dist/internal/distribution.js +3 -1
  121. package/dist/internal/durable-session-store.d.ts +48 -0
  122. package/dist/internal/durable-session-store.d.ts.map +1 -0
  123. package/dist/internal/durable-session-store.js +86 -0
  124. package/dist/internal/hosting.d.ts +1 -1
  125. package/dist/internal/hosting.js +2 -2
  126. package/dist/internal/init-project.d.ts +34 -0
  127. package/dist/internal/init-project.d.ts.map +1 -1
  128. package/dist/internal/init-project.js +121 -2
  129. package/dist/internal/ledger-serving.d.ts +101 -0
  130. package/dist/internal/ledger-serving.d.ts.map +1 -0
  131. package/dist/internal/ledger-serving.js +174 -0
  132. package/dist/internal/listen-port.d.ts +22 -0
  133. package/dist/internal/listen-port.d.ts.map +1 -0
  134. package/dist/internal/listen-port.js +77 -0
  135. package/dist/internal/mcp-oauth.d.ts +12 -1
  136. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  137. package/dist/internal/mcp-oauth.js +25 -3
  138. package/dist/internal/playground/static.d.ts +21 -4
  139. package/dist/internal/playground/static.d.ts.map +1 -1
  140. package/dist/internal/playground/static.js +82 -18
  141. package/dist/internal/sdk-runner.d.ts.map +1 -1
  142. package/dist/internal/sdk-runner.js +10 -1
  143. package/dist/internal/server.d.ts.map +1 -1
  144. package/dist/internal/server.js +67 -49
  145. package/dist/internal/session-engine.d.ts +57 -1
  146. package/dist/internal/session-engine.d.ts.map +1 -1
  147. package/dist/internal/session-engine.js +117 -28
  148. package/dist/internal/slack-provision-client.js +1 -1
  149. package/dist/internal/workspace.d.ts +6 -2
  150. package/dist/internal/workspace.d.ts.map +1 -1
  151. package/dist/internal/workspace.js +10 -2
  152. package/dist/kv/memory-kv.d.ts +23 -0
  153. package/dist/kv/memory-kv.d.ts.map +1 -0
  154. package/dist/kv/memory-kv.js +103 -0
  155. package/dist/kv.d.ts +22 -0
  156. package/dist/kv.d.ts.map +1 -0
  157. package/dist/kv.js +20 -0
  158. package/dist/ledger/session-ledger.d.ts +204 -0
  159. package/dist/ledger/session-ledger.d.ts.map +1 -0
  160. package/dist/ledger/session-ledger.js +382 -0
  161. package/dist/ledger.d.ts +11 -0
  162. package/dist/ledger.d.ts.map +1 -0
  163. package/dist/ledger.js +9 -0
  164. package/dist/playground/assets/index-B3HBneZZ.js +85 -0
  165. package/dist/playground/assets/index-Bqk9nplh.css +1 -0
  166. package/dist/playground/index.html +2 -2
  167. package/dist/storage-backends/cursor-hosted.d.ts +25 -3
  168. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  169. package/dist/storage-backends/cursor-hosted.js +50 -2
  170. package/dist/storage-backends/file-kv.d.ts +5 -2
  171. package/dist/storage-backends/file-kv.d.ts.map +1 -1
  172. package/dist/storage-backends/file-kv.js +107 -7
  173. package/dist/storage.d.ts +60 -5
  174. package/dist/storage.d.ts.map +1 -1
  175. package/dist/storage.js +28 -4
  176. package/dist/types.d.ts +123 -3
  177. package/dist/types.d.ts.map +1 -1
  178. package/dist/types.js +14 -0
  179. package/docs/README.md +10 -7
  180. package/docs/ab.md +2 -2
  181. package/docs/building-with-agents.md +1 -1
  182. package/docs/deployment.md +34 -11
  183. package/docs/evals.md +3 -3
  184. package/docs/example-agents/index.md +1 -1
  185. package/docs/guides/webhooks.md +87 -0
  186. package/docs/quickstart.md +7 -8
  187. package/docs/reference/cli.md +15 -6
  188. package/docs/reference/hooks.md +8 -2
  189. package/docs/storage.md +67 -7
  190. package/docs/troubleshooting.md +10 -2
  191. package/package.json +20 -7
  192. package/skills/create-agent/SKILL.md +1 -1
  193. package/src/bin/agent-serve.ts +27 -11
  194. package/src/channels/slack/api.ts +19 -14
  195. package/src/channels/slack/attachments.ts +635 -0
  196. package/src/channels/slack/cursor-account.ts +8 -0
  197. package/src/channels/slack/defaults.ts +34 -18
  198. package/src/channels/slack/dispatch.ts +121 -6
  199. package/src/channels/slack/inbound.ts +60 -0
  200. package/src/channels/slack/index.ts +1 -0
  201. package/src/channels/slack/live-delivery.ts +17 -3
  202. package/src/channels/slack/manifest.ts +1 -0
  203. package/src/channels/slack/slack-channel.ts +7 -0
  204. package/src/channels/slack/types.ts +47 -1
  205. package/src/index.ts +9 -0
  206. package/src/internal/cli-ax.ts +25 -1
  207. package/src/internal/cli-deploy.ts +22 -4
  208. package/src/internal/cli-mcp-oauth.ts +3 -1
  209. package/src/internal/cli-mcp.ts +4 -4
  210. package/src/internal/cursor-event-relay.ts +168 -0
  211. package/src/internal/deploy-client.ts +1 -1
  212. package/src/internal/discovery.ts +42 -5
  213. package/src/internal/distribution.ts +3 -1
  214. package/src/internal/durable-session-store.ts +95 -0
  215. package/src/internal/hosting.ts +2 -2
  216. package/src/internal/init-project.ts +151 -2
  217. package/src/internal/ledger-serving.ts +240 -0
  218. package/src/internal/listen-port.ts +85 -0
  219. package/src/internal/mcp-oauth.ts +29 -4
  220. package/src/internal/playground/static.ts +111 -20
  221. package/src/internal/sdk-runner.ts +11 -1
  222. package/src/internal/server.ts +76 -62
  223. package/src/internal/session-engine.ts +192 -16
  224. package/src/internal/slack-provision-client.ts +1 -1
  225. package/src/internal/workspace.ts +14 -3
  226. package/src/kv/memory-kv.ts +99 -0
  227. package/src/kv.ts +27 -0
  228. package/src/ledger/session-ledger.ts +535 -0
  229. package/src/ledger.ts +29 -0
  230. package/src/storage-backends/cursor-hosted.ts +108 -3
  231. package/src/storage-backends/file-kv.ts +128 -12
  232. package/src/storage.ts +90 -6
  233. package/src/types.ts +130 -4
  234. package/dist/channels/slack/post-update-delivery.d.ts +0 -85
  235. package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
  236. package/dist/docs/assets/chunks/@localSearchIndexroot.B-VTH4As.js +0 -1
  237. package/dist/docs/assets/example-agents_index.md.DgGBwckv.js +0 -2
  238. package/dist/docs/assets/index.md.CmhptOmN.js +0 -24
  239. package/dist/docs/assets/troubleshooting.md.B5RVX_tL.js +0 -1
  240. package/dist/internal/json-dir-store.d.ts +0 -32
  241. package/dist/internal/json-dir-store.d.ts.map +0 -1
  242. package/dist/internal/persistence-coordinator.d.ts +0 -127
  243. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  244. package/dist/multi-tenant.d.ts +0 -80
  245. package/dist/multi-tenant.d.ts.map +0 -1
  246. package/dist/multi-tenant.js +0 -69
  247. package/dist/persistence.d.ts +0 -184
  248. package/dist/persistence.d.ts.map +0 -1
  249. package/dist/playground/assets/index-BRhBbnd4.css +0 -1
  250. package/dist/playground/assets/index-_2WKw9a-.js +0 -85
  251. package/dist/storage-backends/postgres-kv.d.ts +0 -82
  252. package/dist/storage-backends/postgres-kv.d.ts.map +0 -1
  253. package/dist/storage-backends/postgres-kv.js +0 -185
  254. package/src/storage-backends/postgres-kv.ts +0 -253
  255. /package/dist/docs/assets/{ab.md.6cLOW7--.lean.js → ab.md.hdxsvX5M.lean.js} +0 -0
  256. /package/dist/docs/assets/{building-with-agents.md.txrcGU2B.lean.js → building-with-agents.md.PeZaZA1P.lean.js} +0 -0
  257. /package/dist/docs/assets/{evals.md.BQXI3rXy.lean.js → evals.md.qeoGgPw0.lean.js} +0 -0
  258. /package/dist/docs/assets/{example-agents_index.md.DgGBwckv.lean.js → example-agents_index.md.xmJ87d_3.lean.js} +0 -0
  259. /package/dist/docs/assets/{quickstart.md.C_b6ESpD.lean.js → quickstart.md.rIbfahJM.lean.js} +0 -0
@@ -0,0 +1,520 @@
1
+ /**
2
+ * Download and classify Slack file uploads for agent turns.
3
+ *
4
+ * Images become vision inputs (SDK `images`). Documents and videos land under
5
+ * `slack-attachments/` in the session workspace so the model can Read them.
6
+ */
7
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
+ return new (P || (P = Promise))(function (resolve, reject) {
10
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
14
+ });
15
+ };
16
+ import { callSlackApiSoft, isSlackApiTransport } from "./api.js";
17
+ import { tryResolveBotToken } from "./credentials.js";
18
+ export const SLACK_ATTACHMENTS_DIR = "slack-attachments";
19
+ export const SLACK_ATTACHMENT_ONLY_PROMPT = "The user shared Slack attachment(s) with no message text. Review the attached files.";
20
+ const DEFAULT_MAX_FILE_BYTES = 15 * 1024 * 1024;
21
+ const DEFAULT_MAX_TOTAL_BYTES = 35 * 1024 * 1024;
22
+ const DEFAULT_MAX_FILES = 8;
23
+ const DEFAULT_DOWNLOAD_TIMEOUT_MS = 10000;
24
+ const TEXT_SLACK_FILETYPES = new Set([
25
+ "text",
26
+ "markdown",
27
+ "md",
28
+ "diff",
29
+ "csv",
30
+ "tsv",
31
+ "json",
32
+ "xml",
33
+ "yaml",
34
+ "c",
35
+ "csharp",
36
+ "cpp",
37
+ "css",
38
+ "clojure",
39
+ "coffeescript",
40
+ "dart",
41
+ "dockerfile",
42
+ "email",
43
+ "erlang",
44
+ "fsharp",
45
+ "go",
46
+ "groovy",
47
+ "html",
48
+ "haskell",
49
+ "java",
50
+ "javascript",
51
+ "kotlin",
52
+ "latex",
53
+ "lisp",
54
+ "lua",
55
+ "matlab",
56
+ "objc",
57
+ "ocaml",
58
+ "pascal",
59
+ "perl",
60
+ "php",
61
+ "powershell",
62
+ "python",
63
+ "r",
64
+ "ruby",
65
+ "rust",
66
+ "sql",
67
+ "sass",
68
+ "scala",
69
+ "scheme",
70
+ "shell",
71
+ "swift",
72
+ "typescript",
73
+ "vb",
74
+ "vbscript",
75
+ ]);
76
+ function asRecord(value) {
77
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
78
+ return null;
79
+ }
80
+ return value;
81
+ }
82
+ function asString(value) {
83
+ return typeof value === "string" ? value : undefined;
84
+ }
85
+ function asNumber(value) {
86
+ return typeof value === "number" && Number.isFinite(value)
87
+ ? value
88
+ : undefined;
89
+ }
90
+ function normalizeMimeType(mimeType) {
91
+ return (mimeType !== null && mimeType !== void 0 ? mimeType : "").split(";")[0].trim().toLowerCase();
92
+ }
93
+ function getLowercaseFileExtension(filename) {
94
+ if (filename === undefined || filename.length === 0) {
95
+ return "";
96
+ }
97
+ const extensionStart = filename.lastIndexOf(".");
98
+ if (extensionStart < 0 || extensionStart === filename.length - 1) {
99
+ return "";
100
+ }
101
+ return filename.slice(extensionStart + 1).toLowerCase();
102
+ }
103
+ /** Keep path-safe basename pieces (mirrors cloud-agent sanitizeFilename). */
104
+ export function sanitizeSlackFilename(name) {
105
+ const trimmed = name.trim().replace(/[/\\?%*:|"<>]/g, "_");
106
+ return trimmed.length > 0 ? trimmed.slice(0, 180) : "file";
107
+ }
108
+ export function isSupportedImageFormat(mimeType) {
109
+ return [
110
+ "image/jpeg",
111
+ "image/jpg",
112
+ "image/png",
113
+ "image/gif",
114
+ "image/webp",
115
+ ].includes(normalizeMimeType(mimeType));
116
+ }
117
+ export function isSupportedDocumentFormat(args) {
118
+ const mimeType = normalizeMimeType(args.mimeType);
119
+ const extension = getLowercaseFileExtension(args.filename);
120
+ const supportedExtensions = new Set([
121
+ "7z",
122
+ "bz2",
123
+ "doc",
124
+ "docx",
125
+ "gz",
126
+ "pdf",
127
+ "ppt",
128
+ "pptx",
129
+ "rar",
130
+ "tar",
131
+ "tbz",
132
+ "tbz2",
133
+ "tgz",
134
+ "txt",
135
+ "md",
136
+ "markdown",
137
+ "csv",
138
+ "xlsx",
139
+ "xz",
140
+ "json",
141
+ "jsonl",
142
+ "xml",
143
+ "yaml",
144
+ "yml",
145
+ "toml",
146
+ "txz",
147
+ "log",
148
+ "tzst",
149
+ "zip",
150
+ "zst",
151
+ ]);
152
+ const supportedMimeTypes = new Set([
153
+ "application/gzip",
154
+ "application/msword",
155
+ "application/pdf",
156
+ "application/json",
157
+ "application/jsonl",
158
+ "application/vnd.rar",
159
+ "application/vnd.ms-powerpoint",
160
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
161
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
162
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
163
+ "application/xml",
164
+ "application/x-7z-compressed",
165
+ "application/x-bzip2",
166
+ "application/x-compressed-tar",
167
+ "application/x-gzip",
168
+ "application/x-gtar",
169
+ "application/x-rar-compressed",
170
+ "application/x-tar",
171
+ "application/x-xz",
172
+ "application/x-yaml",
173
+ "application/x-zip-compressed",
174
+ "application/yaml",
175
+ "application/zip",
176
+ "application/toml",
177
+ "application/zstd",
178
+ "application/x-zstd",
179
+ "text/csv",
180
+ "text/markdown",
181
+ "text/plain",
182
+ "text/xml",
183
+ "text/x-log",
184
+ "text/x-markdown",
185
+ "text/yaml",
186
+ ]);
187
+ return (mimeType.startsWith("text/") ||
188
+ supportedMimeTypes.has(mimeType) ||
189
+ supportedExtensions.has(extension));
190
+ }
191
+ export function isSupportedVideoFormat(mimeType) {
192
+ return normalizeMimeType(mimeType).startsWith("video/");
193
+ }
194
+ function hasTextSlackHint(file) {
195
+ if (file.mode === "snippet") {
196
+ return true;
197
+ }
198
+ const filetype = typeof file.filetype === "string" ? file.filetype.trim().toLowerCase() : "";
199
+ return TEXT_SLACK_FILETYPES.has(filetype);
200
+ }
201
+ /**
202
+ * Classify a Slack file as image / document / video. Tolerates empty
203
+ * `mimetype` on snippets (Slack fills it asynchronously).
204
+ */
205
+ export function classifySlackFile(file) {
206
+ const populatedMimeType = typeof file.mimetype === "string" ? normalizeMimeType(file.mimetype) : "";
207
+ const textHint = hasTextSlackHint(file);
208
+ const mimeType = populatedMimeType.length > 0
209
+ ? populatedMimeType
210
+ : textHint
211
+ ? "text/plain"
212
+ : "";
213
+ if (mimeType.length === 0) {
214
+ return undefined;
215
+ }
216
+ if (isSupportedImageFormat(mimeType)) {
217
+ return { kind: "image", mimeType };
218
+ }
219
+ const filename = typeof file.name === "string"
220
+ ? file.name
221
+ : typeof file.title === "string"
222
+ ? file.title
223
+ : undefined;
224
+ const isTextLike = textHint || mimeType.startsWith("text/");
225
+ if (isTextLike || isSupportedDocumentFormat({ mimeType, filename })) {
226
+ return { kind: "document", mimeType, isTextLike };
227
+ }
228
+ if (isSupportedVideoFormat(mimeType)) {
229
+ return { kind: "video", mimeType };
230
+ }
231
+ return undefined;
232
+ }
233
+ function isForwardedShareAttachment(value) {
234
+ const record = asRecord(value);
235
+ return record !== null && record.is_share === true;
236
+ }
237
+ /** Top-level uploads plus files nested in forwarded `is_share` attachments. */
238
+ export function collectSlackMessageFiles(message) {
239
+ const record = asRecord(message);
240
+ if (record === null) {
241
+ return [];
242
+ }
243
+ const files = Array.isArray(record.files)
244
+ ? [...record.files]
245
+ : [];
246
+ if (Array.isArray(record.attachments)) {
247
+ for (const attachment of record.attachments) {
248
+ if (isForwardedShareAttachment(attachment) &&
249
+ Array.isArray(attachment.files)) {
250
+ files.push(...attachment.files);
251
+ }
252
+ }
253
+ }
254
+ return files;
255
+ }
256
+ export function parseSlackFileRefs(message) {
257
+ const files = collectSlackMessageFiles(message);
258
+ const refs = [];
259
+ for (const file of files) {
260
+ const id = asString(file.id);
261
+ if (id === undefined || id.length === 0) {
262
+ continue;
263
+ }
264
+ refs.push({
265
+ id,
266
+ name: asString(file.name),
267
+ title: asString(file.title),
268
+ mimetype: asString(file.mimetype),
269
+ filetype: asString(file.filetype),
270
+ mode: asString(file.mode),
271
+ size: asNumber(file.size),
272
+ dataBase64: asString(file.dataBase64),
273
+ });
274
+ }
275
+ return refs.length > 0 ? refs : undefined;
276
+ }
277
+ function attachmentFilename(file) {
278
+ var _a, _b;
279
+ return sanitizeSlackFilename((_b = (_a = file.name) !== null && _a !== void 0 ? _a : file.title) !== null && _b !== void 0 ? _b : file.id);
280
+ }
281
+ function looksLikeHtml(bytes) {
282
+ const head = Buffer.from(bytes.subarray(0, 100))
283
+ .toString("utf8")
284
+ .toLowerCase();
285
+ return head.includes("<html");
286
+ }
287
+ function isTextLikeBytes(args) {
288
+ if (args.isTextLike || args.mimeType.startsWith("text/")) {
289
+ return true;
290
+ }
291
+ // Reject obvious binary before attempting utf8 decode for docs.
292
+ for (let i = 0; i < Math.min(args.bytes.length, 512); i++) {
293
+ if (args.bytes[i] === 0) {
294
+ return false;
295
+ }
296
+ }
297
+ return (args.mimeType === "application/json" ||
298
+ args.mimeType === "application/jsonl" ||
299
+ args.mimeType === "application/xml" ||
300
+ args.mimeType === "application/yaml" ||
301
+ args.mimeType === "application/x-yaml" ||
302
+ args.mimeType === "application/toml");
303
+ }
304
+ function downloadSlackFileBytes(args) {
305
+ return __awaiter(this, void 0, void 0, function* () {
306
+ var _a, _b, _c;
307
+ // cursorAccount transport cannot mint a Bearer for files.slack.com; the
308
+ // relay must pre-attach `dataBase64` on the file ref instead.
309
+ if (isSlackApiTransport(args.botToken)) {
310
+ return null;
311
+ }
312
+ const info = yield callSlackApiSoft({
313
+ botToken: args.botToken,
314
+ operation: "files.info",
315
+ body: { file: args.fileId },
316
+ });
317
+ if (info.ok !== true) {
318
+ return null;
319
+ }
320
+ const file = asRecord(info.file);
321
+ if (file === null) {
322
+ return null;
323
+ }
324
+ const downloadUrl = asString(file.url_private_download);
325
+ if (downloadUrl === undefined || downloadUrl.length === 0) {
326
+ return null;
327
+ }
328
+ const fileSize = asNumber(file.size);
329
+ if (fileSize !== undefined && fileSize > args.maxBytes) {
330
+ return null;
331
+ }
332
+ const botToken = yield tryResolveBotToken({ botToken: args.botToken });
333
+ const headers = {};
334
+ if (botToken !== undefined && botToken.length > 0) {
335
+ headers.Authorization = `Bearer ${botToken}`;
336
+ }
337
+ let response;
338
+ try {
339
+ response = yield fetch(downloadUrl, {
340
+ headers,
341
+ signal: AbortSignal.timeout(args.downloadTimeoutMs),
342
+ });
343
+ }
344
+ catch (_d) {
345
+ return null;
346
+ }
347
+ if (!response.ok) {
348
+ return null;
349
+ }
350
+ const contentLengthHeader = response.headers.get("content-length");
351
+ if (contentLengthHeader !== null && contentLengthHeader.length > 0) {
352
+ const contentLength = Number.parseInt(contentLengthHeader, 10);
353
+ if (!Number.isNaN(contentLength) && contentLength > args.maxBytes) {
354
+ return null;
355
+ }
356
+ }
357
+ const buffer = Buffer.from(yield response.arrayBuffer());
358
+ if (buffer.length > args.maxBytes) {
359
+ return null;
360
+ }
361
+ const actualContentType = response.headers.get("content-type");
362
+ if (normalizeMimeType(actualContentType !== null && actualContentType !== void 0 ? actualContentType : undefined) === "text/html" ||
363
+ looksLikeHtml(buffer)) {
364
+ return null;
365
+ }
366
+ const responseMimeType = normalizeMimeType(actualContentType !== null && actualContentType !== void 0 ? actualContentType : undefined);
367
+ const metadataMimeType = normalizeMimeType((_a = asString(file.mimetype)) !== null && _a !== void 0 ? _a : args.fallbackMimeType);
368
+ const effectiveMimeType = responseMimeType.length > 0 &&
369
+ responseMimeType !== "application/octet-stream"
370
+ ? responseMimeType
371
+ : metadataMimeType;
372
+ return {
373
+ data: new Uint8Array(buffer),
374
+ mimeType: effectiveMimeType.length > 0 ? effectiveMimeType : args.fallbackMimeType,
375
+ filename: sanitizeSlackFilename((_c = (_b = asString(file.name)) !== null && _b !== void 0 ? _b : asString(file.title)) !== null && _c !== void 0 ? _c : args.fileId),
376
+ };
377
+ });
378
+ }
379
+ /**
380
+ * Download (or use pre-attached) Slack files and split them into vision
381
+ * images + workspace blobs for the turn.
382
+ */
383
+ export function prepareSlackAttachments(options) {
384
+ return __awaiter(this, void 0, void 0, function* () {
385
+ var _a, _b;
386
+ var _c, _d, _e, _f;
387
+ const maxFiles = (_c = options.maxFiles) !== null && _c !== void 0 ? _c : DEFAULT_MAX_FILES;
388
+ const maxFileBytes = (_d = options.maxFileBytes) !== null && _d !== void 0 ? _d : DEFAULT_MAX_FILE_BYTES;
389
+ const maxTotalBytes = (_e = options.maxTotalBytes) !== null && _e !== void 0 ? _e : DEFAULT_MAX_TOTAL_BYTES;
390
+ const downloadTimeoutMs = (_f = options.downloadTimeoutMs) !== null && _f !== void 0 ? _f : DEFAULT_DOWNLOAD_TIMEOUT_MS;
391
+ const loaded = yield Promise.all(options.files.slice(0, maxFiles).map((file) => __awaiter(this, void 0, void 0, function* () {
392
+ var _a, _b;
393
+ const classification = classifySlackFile(file);
394
+ if (classification === undefined) {
395
+ return undefined;
396
+ }
397
+ if (file.size !== undefined && file.size > maxFileBytes) {
398
+ (_a = options.log) === null || _a === void 0 ? void 0 : _a.info("slack attachment skipped (size)", {
399
+ file_id: file.id,
400
+ size: file.size,
401
+ max: maxFileBytes,
402
+ });
403
+ return undefined;
404
+ }
405
+ if (file.dataBase64 !== undefined && file.dataBase64.length > 0) {
406
+ return {
407
+ file,
408
+ classification,
409
+ bytes: new Uint8Array(Buffer.from(file.dataBase64, "base64")),
410
+ mimeType: classification.mimeType,
411
+ filename: attachmentFilename(file),
412
+ };
413
+ }
414
+ const downloaded = yield downloadSlackFileBytes({
415
+ botToken: options.botToken,
416
+ fileId: file.id,
417
+ fallbackMimeType: classification.mimeType,
418
+ maxBytes: maxFileBytes,
419
+ downloadTimeoutMs,
420
+ });
421
+ if (downloaded === null) {
422
+ (_b = options.log) === null || _b === void 0 ? void 0 : _b.info("slack attachment download failed", {
423
+ file_id: file.id,
424
+ kind: classification.kind,
425
+ });
426
+ return undefined;
427
+ }
428
+ return {
429
+ file,
430
+ classification,
431
+ bytes: downloaded.data,
432
+ mimeType: downloaded.mimeType,
433
+ filename: downloaded.filename,
434
+ };
435
+ })));
436
+ const images = [];
437
+ const workspaceFiles = {};
438
+ const annotations = [];
439
+ let totalBytes = 0;
440
+ let imageIndex = 0;
441
+ let documentIndex = 0;
442
+ let videoIndex = 0;
443
+ for (const item of loaded) {
444
+ if (item === undefined) {
445
+ continue;
446
+ }
447
+ const { file, classification, bytes, mimeType, filename } = item;
448
+ if (bytes.length === 0) {
449
+ (_a = options.log) === null || _a === void 0 ? void 0 : _a.info("slack attachment download failed", {
450
+ file_id: file.id,
451
+ kind: classification.kind,
452
+ });
453
+ continue;
454
+ }
455
+ if (bytes.length > maxFileBytes ||
456
+ totalBytes + bytes.length > maxTotalBytes) {
457
+ (_b = options.log) === null || _b === void 0 ? void 0 : _b.info("slack attachment skipped (budget)", {
458
+ file_id: file.id,
459
+ bytes: bytes.length,
460
+ });
461
+ continue;
462
+ }
463
+ totalBytes += bytes.length;
464
+ if (classification.kind === "image") {
465
+ if (!isSupportedImageFormat(mimeType)) {
466
+ continue;
467
+ }
468
+ imageIndex++;
469
+ images.push({
470
+ data: Buffer.from(bytes).toString("base64"),
471
+ mimeType,
472
+ });
473
+ annotations.push(`[Image ${imageIndex}: ${filename}]`);
474
+ continue;
475
+ }
476
+ const relativePath = `${SLACK_ATTACHMENTS_DIR}/${filename}`;
477
+ const textLike = classification.kind === "document" &&
478
+ isTextLikeBytes({
479
+ mimeType,
480
+ isTextLike: classification.isTextLike,
481
+ bytes,
482
+ });
483
+ if (textLike) {
484
+ workspaceFiles[relativePath] = Buffer.from(bytes).toString("utf8");
485
+ }
486
+ else {
487
+ workspaceFiles[relativePath] = {
488
+ encoding: "base64",
489
+ data: Buffer.from(bytes).toString("base64"),
490
+ mimeType,
491
+ };
492
+ }
493
+ if (classification.kind === "document") {
494
+ documentIndex++;
495
+ annotations.push(`[Document ${documentIndex}: ${filename} → ${relativePath}]`);
496
+ }
497
+ else {
498
+ videoIndex++;
499
+ annotations.push(`[Video ${videoIndex}: ${filename} → ${relativePath}]`);
500
+ }
501
+ }
502
+ return { images, workspaceFiles, annotations };
503
+ });
504
+ }
505
+ /** Merge attachment annotations into the user prompt. */
506
+ export function appendSlackAttachmentAnnotations(prompt, annotations) {
507
+ if (annotations.length === 0) {
508
+ return prompt;
509
+ }
510
+ const block = [
511
+ "<slack_attachments>",
512
+ ...annotations,
513
+ "</slack_attachments>",
514
+ ].join("\n");
515
+ const trimmed = prompt.trim();
516
+ if (trimmed.length === 0) {
517
+ return `${block}\n\n${SLACK_ATTACHMENT_ONLY_PROMPT}`;
518
+ }
519
+ return `${block}\n\n${trimmed}`;
520
+ }
@@ -14,7 +14,7 @@
14
14
  import type { ArtifactsApi, HostContext, SendMessageFn } from "../../types.js";
15
15
  import type { EventIdDedupe } from "./event-id-dedupe.js";
16
16
  import type { SlackLog } from "./log.js";
17
- import type { SlackApiTransport, SlackChannelOptions, SlackCredentials, SlackMessageHandler } from "./types.js";
17
+ import type { SlackApiTransport, SlackChannelOptions, SlackCredentials, SlackMessageFile, SlackMessageHandler } from "./types.js";
18
18
  /**
19
19
  * Transport placeholder created at `slackChannel()` definition time and
20
20
  * bound to the real backend proxy caller when the serve host starts its
@@ -45,6 +45,12 @@ export interface CursorSlackEnvelope {
45
45
  matchedBy: "name" | "thread";
46
46
  eventId?: string;
47
47
  receivedAtMs: number;
48
+ /**
49
+ * Slack file uploads on the triggering message. When `dataBase64` is set
50
+ * the Cursor backend already downloaded bytes (cursorAccount has no bot
51
+ * token for files.slack.com).
52
+ */
53
+ files?: SlackMessageFile[];
48
54
  }
49
55
  /** Serve-time runtime handle for one cursor-account Slack channel. */
50
56
  export interface SlackCursorAccountRuntime {
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-account.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/cursor-account.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EACV,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAEhB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAC7C;AAED,wBAAgB,8BAA8B,IAAI,wBAAwB,CAezE;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,SAAS,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sEAAsE;AACtE,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,0EAA0E;IAC1E,WAAW,EAAE,gBAAgB,CAAC;IAC9B,SAAS,EAAE,wBAAwB,CAAC;IACpC,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,EAAE,mBAAmB,CAAC;IAClC,eAAe,EAAE,mBAAmB,CAAC;IACrC,MAAM,EAAE,MAAM,QAAQ,CAAC;CACxB;AAID,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,yBAAyB,GACjC,IAAI,CAEN;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,GACjB,yBAAyB,GAAG,SAAS,CAEvC;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,OAAO,EAAE,yBAAyB,CAAC;IACnC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,YAAY,CAAC;IACxB,sBAAsB,CAAC,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,kBAAkB,CAAC,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrE,+BAA+B,CAAC,EAAE,CAChC,iBAAiB,EAAE,MAAM,KACtB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjC,qBAAqB,CAAC,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACzE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqEhB"}
1
+ {"version":3,"file":"cursor-account.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/cursor-account.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EACV,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAEhB,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAC7C;AAED,wBAAgB,8BAA8B,IAAI,wBAAwB,CAezE;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,SAAS,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED,sEAAsE;AACtE,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,0EAA0E;IAC1E,WAAW,EAAE,gBAAgB,CAAC;IAC9B,SAAS,EAAE,wBAAwB,CAAC;IACpC,QAAQ,EAAE,aAAa,CAAC;IACxB,YAAY,EAAE,mBAAmB,CAAC;IAClC,eAAe,EAAE,mBAAmB,CAAC;IACrC,MAAM,EAAE,MAAM,QAAQ,CAAC;CACxB;AAID,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,yBAAyB,GACjC,IAAI,CAEN;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,GACjB,yBAAyB,GAAG,SAAS,CAEvC;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,CAAC,IAAI,EAAE;IACtD,OAAO,EAAE,yBAAyB,CAAC;IACnC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,YAAY,CAAC;IACxB,sBAAsB,CAAC,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,kBAAkB,CAAC,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrE,+BAA+B,CAAC,EAAE,CAChC,iBAAiB,EAAE,MAAM,KACtB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjC,qBAAqB,CAAC,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACzE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEhB"}
@@ -67,11 +67,11 @@ export function dispatchCursorSlackEnvelope(args) {
67
67
  });
68
68
  return;
69
69
  }
70
- const message = Object.assign({ text: envelope.text, body: envelope.text, markdown: slackTextToMarkdown(envelope.text), ts: envelope.messageTs, threadTs: envelope.threadTs, channelId: envelope.channelId, teamId: envelope.slackTeamId }, (envelope.slackUserId === undefined
70
+ const message = Object.assign(Object.assign({ text: envelope.text, body: envelope.text, markdown: slackTextToMarkdown(envelope.text), ts: envelope.messageTs, threadTs: envelope.threadTs, channelId: envelope.channelId, teamId: envelope.slackTeamId }, (envelope.slackUserId === undefined
71
71
  ? {}
72
72
  : {
73
73
  author: { userId: envelope.slackUserId, isBot: false },
74
- }));
74
+ })), { files: envelope.files });
75
75
  // DMs to @Cursor route as direct messages; everything else (explicit
76
76
  // `@Cursor <agent> …` mentions and claimed-thread replies) dispatches on
77
77
  // the mention handler — a claimed-thread reply is addressed to the agent
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/defaults.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAe3D,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAE1D,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAMpB,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,GAAG,SAAS,GACpC,cAAc,CAEhB;AAED,qCAAqC;AACrC,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,iBAAiB,GAAG,SAAS,GACpC,OAAO,CAET;AAED,6EAA6E;AAC7E,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAS7E;AA0BD,6EAA6E;AAC7E,wBAAgB,sBAAsB,CACpC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,EACnC,MAAM,CAAC,EAAE,iBAAiB,GACzB,mBAAmB,CAQrB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,EACnC,MAAM,CAAC,EAAE,iBAAiB,GACzB,mBAAmB,CAWrB;AAED,eAAO,MAAM,mBAAmB,EAAE,mBACR,CAAC;AAC3B,eAAO,MAAM,sBAAsB,EAAE,mBACX,CAAC;AAC3B,eAAO,MAAM,oBAAoB,EAAE,mBACL,CAAC;AAE/B,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mEAAmE;IACnE,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,QAAQ,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,oBAAoB,CAAC,iBAAiB,CAAC,CA8XzC"}
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/defaults.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAe3D,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAE1D,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAMpB,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,GAAG,SAAS,GACpC,cAAc,CAEhB;AAED,qCAAqC;AACrC,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,iBAAiB,GAAG,SAAS,GACpC,OAAO,CAET;AAED,6EAA6E;AAC7E,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAS7E;AA0BD,6EAA6E;AAC7E,wBAAgB,sBAAsB,CACpC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,EACnC,MAAM,CAAC,EAAE,iBAAiB,GACzB,mBAAmB,CAQrB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,EACnC,MAAM,CAAC,EAAE,iBAAiB,GACzB,mBAAmB,CAWrB;AAED,eAAO,MAAM,mBAAmB,EAAE,mBACR,CAAC;AAC3B,eAAO,MAAM,sBAAsB,EAAE,mBACX,CAAC;AAC3B,eAAO,MAAM,oBAAoB,EAAE,mBACL,CAAC;AAE/B,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mEAAmE;IACnE,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,QAAQ,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,oBAAoB,CAAC,iBAAiB,CAAC,CA8YzC"}
@@ -106,6 +106,21 @@ export function buildDefaultEvents(options = {}) {
106
106
  const botToken = credentials === null || credentials === void 0 ? void 0 : credentials.botToken;
107
107
  const getLog = (_b = options.getLog) !== null && _b !== void 0 ? _b : (() => defaultSlackLog);
108
108
  const strategy = createStreamDeliveryStrategy({ botToken, getLog });
109
+ const clearWorkingStatus = (channelId, threadTs) => __awaiter(this, void 0, void 0, function* () {
110
+ const result = yield setThreadStatus({
111
+ botToken,
112
+ channelId,
113
+ threadTs,
114
+ status: "",
115
+ });
116
+ if (!result.ok) {
117
+ getLog().warn("assistant.threads.setStatus not-ok", {
118
+ error: result.error,
119
+ channel: channelId,
120
+ thread_ts: threadTs,
121
+ });
122
+ }
123
+ });
109
124
  return {
110
125
  "turn.started"(event, channel) {
111
126
  return __awaiter(this, void 0, void 0, function* () {
@@ -297,14 +312,6 @@ export function buildDefaultEvents(options = {}) {
297
312
  if (state.channelId === null || state.threadTs === null) {
298
313
  return;
299
314
  }
300
- const clearStatus = () => __awaiter(this, void 0, void 0, function* () {
301
- yield setThreadStatus({
302
- botToken,
303
- channelId: state.channelId,
304
- threadTs: state.threadTs,
305
- status: "",
306
- });
307
- });
308
315
  // Empty final text: still close any open stream / clear the skeleton
309
316
  // and clear typing.
310
317
  if (event.data.text === "") {
@@ -316,7 +323,7 @@ export function buildDefaultEvents(options = {}) {
316
323
  });
317
324
  channel.state.streamTs = null;
318
325
  }
319
- yield clearStatus();
326
+ yield clearWorkingStatus(state.channelId, state.threadTs);
320
327
  return;
321
328
  }
322
329
  const canStream = streaming &&
@@ -332,7 +339,7 @@ export function buildDefaultEvents(options = {}) {
332
339
  sessionId: event.sessionId,
333
340
  });
334
341
  if (delivered) {
335
- yield clearStatus();
342
+ yield clearWorkingStatus(state.channelId, state.threadTs);
336
343
  return;
337
344
  }
338
345
  }
@@ -360,7 +367,7 @@ export function buildDefaultEvents(options = {}) {
360
367
  session: event.sessionId,
361
368
  });
362
369
  }
363
- yield clearStatus();
370
+ yield clearWorkingStatus(state.channelId, state.threadTs);
364
371
  });
365
372
  },
366
373
  "turn.completed"(event, channel) {
@@ -373,6 +380,13 @@ export function buildDefaultEvents(options = {}) {
373
380
  thread_ts: channel.state.threadTs,
374
381
  });
375
382
  yield strategy.cleanupOrphan({ channel });
383
+ // Always drop the assistant chip. message.completed already clears
384
+ // after a normal reply, but a tool-call-only finish (or a missed
385
+ // final assistant message) never hits that path — leaving
386
+ // "Working…" stuck until Slack expires it.
387
+ if (channel.state.channelId !== null && channel.state.threadTs !== null) {
388
+ yield clearWorkingStatus(channel.state.channelId, channel.state.threadTs);
389
+ }
376
390
  if (suggestedPrompts.length === 0 ||
377
391
  channel.state.channelId === null ||
378
392
  channel.state.threadTs === null) {
@@ -426,12 +440,7 @@ export function buildDefaultEvents(options = {}) {
426
440
  });
427
441
  }
428
442
  }
429
- yield setThreadStatus({
430
- botToken,
431
- channelId: state.channelId,
432
- threadTs: state.threadTs,
433
- status: "",
434
- });
443
+ yield clearWorkingStatus(state.channelId, state.threadTs);
435
444
  });
436
445
  },
437
446
  };