@cursor/july 0.1.94 → 0.1.96

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 (332) hide show
  1. package/AGENTS.md +3 -4
  2. package/dist/channels/deployments/types.d.ts +1 -1
  3. package/dist/channels/deployments/types.d.ts.map +1 -1
  4. package/dist/channels/deployments/types.js +1 -0
  5. package/dist/channels/github/github-channel.d.ts.map +1 -1
  6. package/dist/channels/github/github-channel.js +14 -2
  7. package/dist/channels/github/types.d.ts +18 -3
  8. package/dist/channels/github/types.d.ts.map +1 -1
  9. package/dist/channels/origin/api.d.ts +59 -13
  10. package/dist/channels/origin/api.d.ts.map +1 -1
  11. package/dist/channels/origin/api.js +168 -27
  12. package/dist/channels/origin/binding.d.ts +2 -2
  13. package/dist/channels/origin/checks.d.ts +6 -6
  14. package/dist/channels/origin/checks.js +6 -6
  15. package/dist/channels/origin/cursor-account.d.ts +5 -3
  16. package/dist/channels/origin/cursor-account.d.ts.map +1 -1
  17. package/dist/channels/origin/cursor-account.js +5 -3
  18. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  19. package/dist/channels/origin/origin-channel.js +11 -3
  20. package/dist/channels/origin/origin-webhook.d.ts +11 -1
  21. package/dist/channels/origin/origin-webhook.d.ts.map +1 -1
  22. package/dist/channels/origin/origin-webhook.js +23 -3
  23. package/dist/channels/origin/types.d.ts +8 -0
  24. package/dist/channels/origin/types.d.ts.map +1 -1
  25. package/dist/channels/slack/api.d.ts +9 -0
  26. package/dist/channels/slack/api.d.ts.map +1 -1
  27. package/dist/channels/slack/api.js +36 -5
  28. package/dist/channels/slack/defaults.d.ts.map +1 -1
  29. package/dist/channels/slack/defaults.js +17 -46
  30. package/dist/channels/slack/dispatch.d.ts +7 -1
  31. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  32. package/dist/channels/slack/dispatch.js +47 -23
  33. package/dist/channels/slack/inbound.d.ts +8 -0
  34. package/dist/channels/slack/inbound.d.ts.map +1 -1
  35. package/dist/channels/slack/inbound.js +32 -0
  36. package/dist/channels/slack/live-delivery.d.ts +26 -29
  37. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  38. package/dist/channels/slack/live-delivery.js +172 -73
  39. package/dist/channels/slack/manifest.d.ts.map +1 -1
  40. package/dist/channels/slack/manifest.js +5 -1
  41. package/dist/channels/slack/nudge-carrier.d.ts +2 -0
  42. package/dist/channels/slack/nudge-carrier.d.ts.map +1 -1
  43. package/dist/channels/slack/nudge-carrier.js +48 -1
  44. package/dist/channels/slack/pr-ref.d.ts +7 -1
  45. package/dist/channels/slack/pr-ref.d.ts.map +1 -1
  46. package/dist/channels/slack/pr-ref.js +42 -23
  47. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  48. package/dist/channels/slack/slack-channel.js +2 -2
  49. package/dist/docs/404.html +2 -2
  50. package/dist/docs/ab.html +4 -4
  51. package/dist/docs/assets/{app.CFDEas4I.js → app.jXleCmOc.js} +1 -1
  52. package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +1 -0
  53. package/dist/docs/assets/chunks/{VPLocalSearchBox.B1IIYpYS.js → VPLocalSearchBox.8LhyglIT.js} +1 -1
  54. package/dist/docs/assets/chunks/{theme.Ct4NSiLm.js → theme.9ged-pr2.js} +2 -2
  55. package/dist/docs/assets/index.md.CI93Ltlb.js +5 -0
  56. package/dist/docs/assets/{index.md.DRakGHFe.lean.js → index.md.CI93Ltlb.lean.js} +1 -1
  57. package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.js → reference_cli.md.CCnG-nJx.js} +4 -2
  58. package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.lean.js → reference_cli.md.CCnG-nJx.lean.js} +1 -1
  59. package/dist/docs/assets/{reference_skills.md.8son6Hjm.js → reference_skills.md.BFW9retM.js} +3 -3
  60. package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.js → templates_agentic-owners.md.BZSH4N9z.js} +1 -1
  61. package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.js +2 -0
  62. package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.lean.js +1 -0
  63. package/dist/docs/assets/{templates_demo.md.DhFcWN6j.js → templates_demo.md.BeQX9V3H.js} +1 -1
  64. package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.js → templates_pr-autofixer.md.x5zl6-GT.js} +2 -2
  65. package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.lean.js → templates_pr-autofixer.md.x5zl6-GT.lean.js} +1 -1
  66. package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.js +4 -0
  67. package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.lean.js +1 -0
  68. package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.js → templates_security-reviewer.md.lshxbCLK.js} +2 -2
  69. package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.lean.js → templates_security-reviewer.md.lshxbCLK.lean.js} +1 -1
  70. package/dist/docs/assets/{templates_triage.md.CVlpctKS.js → templates_triage.md.Co4UNzkZ.js} +3 -3
  71. package/dist/docs/assets/{templates_triage.md.CVlpctKS.lean.js → templates_triage.md.Co4UNzkZ.lean.js} +1 -1
  72. package/dist/docs/building-with-agents.html +4 -4
  73. package/dist/docs/concepts.html +4 -4
  74. package/dist/docs/deployment.html +4 -4
  75. package/dist/docs/evals.html +4 -4
  76. package/dist/docs/guides/agent-to-agent.html +4 -4
  77. package/dist/docs/guides/cloud-runtime.html +4 -4
  78. package/dist/docs/guides/convert-automation.html +4 -4
  79. package/dist/docs/guides/github.html +4 -4
  80. package/dist/docs/guides/human-in-the-loop.html +4 -4
  81. package/dist/docs/guides/mcp-oauth.html +4 -4
  82. package/dist/docs/guides/opentelemetry.html +4 -4
  83. package/dist/docs/guides/slack.html +4 -4
  84. package/dist/docs/guides/webhooks.html +4 -4
  85. package/dist/docs/hashmap.json +1 -1
  86. package/dist/docs/hillclimbing.html +4 -4
  87. package/dist/docs/index.html +6 -6
  88. package/dist/docs/index.md +5 -1
  89. package/dist/docs/llms-full.txt +189 -26
  90. package/dist/docs/llms.txt +2 -0
  91. package/dist/docs/quickstart.html +4 -4
  92. package/dist/docs/reference/agent-config.html +4 -4
  93. package/dist/docs/reference/artifacts.html +4 -4
  94. package/dist/docs/reference/channels.html +4 -4
  95. package/dist/docs/reference/cli.html +8 -6
  96. package/dist/docs/reference/cli.md +4 -1
  97. package/dist/docs/reference/connections.html +4 -4
  98. package/dist/docs/reference/hooks.html +4 -4
  99. package/dist/docs/reference/http-api.html +4 -4
  100. package/dist/docs/reference/instructions.html +4 -4
  101. package/dist/docs/reference/playground.html +4 -4
  102. package/dist/docs/reference/project-layout.html +4 -4
  103. package/dist/docs/reference/prompt.html +4 -4
  104. package/dist/docs/reference/schedules.html +4 -4
  105. package/dist/docs/reference/sessions.html +4 -4
  106. package/dist/docs/reference/skills.html +7 -7
  107. package/dist/docs/reference/subagents.html +4 -4
  108. package/dist/docs/reference/tools.html +4 -4
  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 +7 -7
  112. package/dist/docs/templates/agentic-owners.md +1 -1
  113. package/dist/docs/templates/code-wiki.html +28 -0
  114. package/dist/docs/templates/code-wiki.md +91 -0
  115. package/dist/docs/templates/demo.html +6 -6
  116. package/dist/docs/templates/demo.md +3 -2
  117. package/dist/docs/templates/pr-autofixer.html +6 -6
  118. package/dist/docs/templates/pr-autofixer.md +7 -13
  119. package/dist/docs/templates/security-help.html +30 -0
  120. package/dist/docs/templates/security-help.md +65 -0
  121. package/dist/docs/templates/security-reviewer.html +6 -6
  122. package/dist/docs/templates/security-reviewer.md +1 -2
  123. package/dist/docs/templates/triage.html +7 -7
  124. package/dist/docs/templates/triage.md +2 -6
  125. package/dist/docs/troubleshooting.html +4 -4
  126. package/dist/files-backends/cursor-hosted.d.ts +26 -0
  127. package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
  128. package/dist/files-backends/cursor-hosted.js +32 -4
  129. package/dist/files.d.ts +2 -0
  130. package/dist/files.d.ts.map +1 -1
  131. package/dist/files.js +5 -0
  132. package/dist/internal/advertise-tools.d.ts +2 -0
  133. package/dist/internal/advertise-tools.d.ts.map +1 -1
  134. package/dist/internal/advertise-tools.js +15 -1
  135. package/dist/internal/bounded.d.ts +7 -0
  136. package/dist/internal/bounded.d.ts.map +1 -0
  137. package/dist/internal/bounded.js +32 -0
  138. package/dist/internal/cli-deploy.d.ts.map +1 -1
  139. package/dist/internal/cli-deploy.js +142 -7
  140. package/dist/internal/conversation-mirror.d.ts +85 -0
  141. package/dist/internal/conversation-mirror.d.ts.map +1 -0
  142. package/dist/internal/conversation-mirror.js +312 -0
  143. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  144. package/dist/internal/cursor/backend-client.js +22 -48
  145. package/dist/internal/cursor/session-token.d.ts +45 -0
  146. package/dist/internal/cursor/session-token.d.ts.map +1 -0
  147. package/dist/internal/cursor/session-token.js +99 -0
  148. package/dist/internal/cursor-agent-template.d.ts +1 -1
  149. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  150. package/dist/internal/cursor-agent-template.js +1 -0
  151. package/dist/internal/deferred-channel-session.d.ts +20 -0
  152. package/dist/internal/deferred-channel-session.d.ts.map +1 -0
  153. package/dist/internal/deferred-channel-session.js +62 -0
  154. package/dist/internal/deploy-client.d.ts +13 -1
  155. package/dist/internal/deploy-client.d.ts.map +1 -1
  156. package/dist/internal/deploy-client.js +11 -1
  157. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  158. package/dist/internal/deploy-manifest.js +11 -5
  159. package/dist/internal/discovery.d.ts.map +1 -1
  160. package/dist/internal/discovery.js +22 -2
  161. package/dist/internal/framework-file-storage.d.ts +61 -0
  162. package/dist/internal/framework-file-storage.d.ts.map +1 -0
  163. package/dist/internal/framework-file-storage.js +204 -0
  164. package/dist/internal/framework-storage-selection.d.ts +38 -16
  165. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  166. package/dist/internal/framework-storage-selection.js +78 -17
  167. package/dist/internal/hosted-admission-context.d.ts +20 -0
  168. package/dist/internal/hosted-admission-context.d.ts.map +1 -0
  169. package/dist/internal/hosted-admission-context.js +31 -0
  170. package/dist/internal/hosted-delivery-protocol.d.ts +5 -0
  171. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  172. package/dist/internal/hosted-delivery-protocol.js +33 -1
  173. package/dist/internal/hosted-delivery.d.ts +11 -2
  174. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  175. package/dist/internal/hosted-delivery.js +79 -35
  176. package/dist/internal/hosted-managed-automation.d.ts +30 -0
  177. package/dist/internal/hosted-managed-automation.d.ts.map +1 -0
  178. package/dist/internal/hosted-managed-automation.js +58 -0
  179. package/dist/internal/init-project.d.ts.map +1 -1
  180. package/dist/internal/init-project.js +2 -0
  181. package/dist/internal/install-cursor-skills.d.ts.map +1 -1
  182. package/dist/internal/install-cursor-skills.js +1 -3
  183. package/dist/internal/managed-delivery.d.ts +80 -0
  184. package/dist/internal/managed-delivery.d.ts.map +1 -0
  185. package/dist/internal/managed-delivery.js +86 -0
  186. package/dist/internal/review-comments.d.ts.map +1 -1
  187. package/dist/internal/review-comments.js +10 -98
  188. package/dist/internal/runtime-dispatch-runner.d.ts +5 -1
  189. package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -1
  190. package/dist/internal/runtime-dispatch-runner.js +7 -0
  191. package/dist/internal/scm/pr-url.d.ts +14 -0
  192. package/dist/internal/scm/pr-url.d.ts.map +1 -0
  193. package/dist/internal/scm/pr-url.js +65 -0
  194. package/dist/internal/sdk-runner.d.ts +25 -0
  195. package/dist/internal/sdk-runner.d.ts.map +1 -1
  196. package/dist/internal/sdk-runner.js +70 -0
  197. package/dist/internal/server.d.ts.map +1 -1
  198. package/dist/internal/server.js +11 -2
  199. package/dist/internal/session-engine.d.ts +13 -0
  200. package/dist/internal/session-engine.d.ts.map +1 -1
  201. package/dist/internal/session-engine.js +71 -3
  202. package/dist/internal/steering.d.ts +15 -0
  203. package/dist/internal/steering.d.ts.map +1 -0
  204. package/dist/internal/steering.js +18 -0
  205. package/dist/playground/assets/index-B1DDvBeC.css +1 -0
  206. package/dist/playground/assets/{index-B3JCyigB.js → index-Cx9CclK_.js} +47 -47
  207. package/dist/playground/index.html +2 -2
  208. package/dist/storage-backends/cursor-hosted.d.ts +8 -0
  209. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  210. package/dist/storage-backends/cursor-hosted.js +4 -3
  211. package/dist/types.d.ts +2 -1
  212. package/dist/types.d.ts.map +1 -1
  213. package/docs/README.md +5 -1
  214. package/docs/reference/cli.md +4 -1
  215. package/docs/templates/agentic-owners.md +1 -1
  216. package/docs/templates/code-wiki.md +96 -0
  217. package/docs/templates/demo.md +3 -2
  218. package/docs/templates/pr-autofixer.md +7 -13
  219. package/docs/templates/security-help.md +70 -0
  220. package/docs/templates/security-reviewer.md +1 -2
  221. package/docs/templates/triage.md +2 -6
  222. package/package.json +2 -2
  223. package/skills/ab/SKILL.md +13 -97
  224. package/skills/create-agent/SKILL.md +123 -223
  225. package/skills/debug/SKILL.md +34 -68
  226. package/skills/evals/SKILL.md +55 -180
  227. package/skills/framework-map/SKILL.md +83 -145
  228. package/skills/github/SKILL.md +53 -111
  229. package/skills/hillclimb/SKILL.md +40 -163
  230. package/skills/mcp-auth/SKILL.md +48 -107
  231. package/skills/otel/SKILL.md +22 -68
  232. package/skills/setup-slack/SKILL.md +77 -208
  233. package/src/channels/deployments/types.ts +1 -0
  234. package/src/channels/github/github-channel.ts +23 -8
  235. package/src/channels/github/types.ts +19 -2
  236. package/src/channels/origin/api.ts +242 -30
  237. package/src/channels/origin/binding.ts +2 -2
  238. package/src/channels/origin/checks.ts +6 -6
  239. package/src/channels/origin/cursor-account.ts +5 -3
  240. package/src/channels/origin/origin-channel.ts +13 -1
  241. package/src/channels/origin/origin-webhook.ts +27 -3
  242. package/src/channels/origin/types.ts +8 -0
  243. package/src/channels/slack/api.ts +41 -5
  244. package/src/channels/slack/defaults.ts +15 -49
  245. package/src/channels/slack/dispatch.ts +56 -22
  246. package/src/channels/slack/inbound.ts +43 -0
  247. package/src/channels/slack/live-delivery.ts +235 -114
  248. package/src/channels/slack/manifest.ts +5 -1
  249. package/src/channels/slack/nudge-carrier.ts +48 -1
  250. package/src/channels/slack/pr-ref.ts +56 -25
  251. package/src/channels/slack/slack-channel.ts +2 -0
  252. package/src/files-backends/cursor-hosted.ts +41 -2
  253. package/src/files.ts +11 -0
  254. package/src/internal/advertise-tools.ts +18 -2
  255. package/src/internal/bounded.ts +28 -0
  256. package/src/internal/cli-deploy.ts +183 -7
  257. package/src/internal/conversation-mirror.ts +406 -0
  258. package/src/internal/cursor/backend-client.ts +34 -58
  259. package/src/internal/cursor/session-token.ts +115 -0
  260. package/src/internal/cursor-agent-template.ts +1 -0
  261. package/src/internal/deferred-channel-session.ts +61 -0
  262. package/src/internal/deploy-client.ts +24 -1
  263. package/src/internal/deploy-manifest.ts +10 -5
  264. package/src/internal/discovery.ts +25 -2
  265. package/src/internal/framework-file-storage.ts +222 -0
  266. package/src/internal/framework-storage-selection.ts +101 -19
  267. package/src/internal/hosted-admission-context.ts +37 -0
  268. package/src/internal/hosted-delivery-protocol.ts +44 -1
  269. package/src/internal/hosted-delivery.ts +105 -40
  270. package/src/internal/hosted-managed-automation.ts +72 -0
  271. package/src/internal/init-project.ts +2 -0
  272. package/src/internal/install-cursor-skills.ts +1 -4
  273. package/src/internal/managed-delivery.ts +125 -0
  274. package/src/internal/review-comments.ts +10 -113
  275. package/src/internal/runtime-dispatch-runner.ts +10 -0
  276. package/src/internal/scm/pr-url.ts +95 -0
  277. package/src/internal/sdk-runner.ts +103 -0
  278. package/src/internal/server.ts +12 -0
  279. package/src/internal/session-engine.ts +76 -4
  280. package/src/internal/steering.ts +17 -0
  281. package/src/storage-backends/cursor-hosted.ts +10 -1
  282. package/src/types.ts +2 -1
  283. package/templates/agentic-owners/README.md +1 -1
  284. package/templates/agentic-owners/agent/agent.ts +0 -10
  285. package/templates/agentic-owners/agent/channels/github.ts +5 -14
  286. package/templates/agentic-owners/agent/lib/config.ts +0 -8
  287. package/templates/agentic-owners/agent/lib/review.ts +2 -15
  288. package/templates/agentic-owners/agent/tools/record_review.ts +2 -4
  289. package/templates/code-wiki/README.md +34 -0
  290. package/templates/code-wiki/agent/agent.ts +8 -0
  291. package/templates/code-wiki/agent/channels/github.ts +144 -0
  292. package/templates/code-wiki/agent/instructions.md +39 -0
  293. package/templates/code-wiki/agent/lib/repos.ts +5 -0
  294. package/templates/code-wiki/agent/lib/wiki.ts +22 -0
  295. package/templates/code-wiki/agent/tools/apply_wiki.ts +219 -0
  296. package/templates/code-wiki/evals/evals.config.ts +5 -0
  297. package/templates/code-wiki/evals/wiki.eval.ts +132 -0
  298. package/templates/code-wiki/init.json +25 -0
  299. package/templates/code-wiki/package.json +18 -0
  300. package/templates/code-wiki/tsconfig.json +12 -0
  301. package/templates/demo/agent/agent.ts +0 -10
  302. package/templates/pr-autofixer/README.md +0 -2
  303. package/templates/pr-autofixer/agent/agent.ts +0 -11
  304. package/templates/pr-autofixer/agent/channels/slack.ts +0 -7
  305. package/templates/pr-autofixer/agent/lib/pr-state.ts +5 -17
  306. package/templates/pr-autofixer/agent/lib/repos.ts +0 -1
  307. package/templates/security-help/README.md +2 -2
  308. package/templates/security-help/agent/agent.ts +1 -2
  309. package/templates/security-help/agent/channels/slack.ts +0 -3
  310. package/templates/security-help/agent/instructions.md +9 -10
  311. package/templates/security-help/agent/skills/access-request.md +1 -1
  312. package/templates/security-help/agent/skills/faq.md +31 -0
  313. package/templates/security-help/agent/skills/security-playbooks.md +1 -1
  314. package/templates/security-help/package.json +1 -2
  315. package/templates/security-reviewer/agent/agent.ts +0 -10
  316. package/templates/triage/agent/agent.ts +0 -10
  317. package/templates/triage/agent/channels/queue.ts +1 -1
  318. package/templates/triage/agent/channels/webhook.ts +1 -3
  319. package/dist/docs/assets/chunks/@localSearchIndexroot.DU3U2Ij2.js +0 -1
  320. package/dist/docs/assets/index.md.DRakGHFe.js +0 -5
  321. package/dist/internal/deployment-scm-event-stream.d.ts +0 -116
  322. package/dist/internal/deployment-scm-event-stream.d.ts.map +0 -1
  323. package/dist/internal/deployment-scm-event-stream.js +0 -474
  324. package/dist/playground/assets/index-D9N7-q97.css +0 -1
  325. package/src/internal/deployment-scm-event-stream.ts +0 -593
  326. package/templates/security-help/agent/knowledge/faq/approvals.md +0 -5
  327. package/templates/security-help/agent/knowledge/faq/channels.md +0 -6
  328. package/templates/security-help/agent/knowledge/faq/phishing.md +0 -10
  329. package/templates/security-help/agent/skills/security-first-pass.md +0 -15
  330. /package/dist/docs/assets/{reference_skills.md.8son6Hjm.lean.js → reference_skills.md.BFW9retM.lean.js} +0 -0
  331. /package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.lean.js → templates_agentic-owners.md.BZSH4N9z.lean.js} +0 -0
  332. /package/dist/docs/assets/{templates_demo.md.DhFcWN6j.lean.js → templates_demo.md.BeQX9V3H.lean.js} +0 -0
@@ -1,22 +1,37 @@
1
1
  /**
2
2
  * Origin public REST helpers (`https://api.cursor.com/v1/origin/...`).
3
3
  *
4
- * Origin writes such as CI check runs are installation-only: they need an
5
- * `oit_…` installation token minted from Origin app credentials (EdDSA app
6
- * JWT → `/v1/origin/app/installations/{id}/access_tokens`). This module
7
- * consolidates that flow so agents configure credentials once and call
8
- * {@link createOriginClient} — the GitHub-channel analogue of
4
+ * This module consolidates Origin auth so agents configure credentials once
5
+ * and call {@link createOriginClient} the GitHub-channel analogue of
9
6
  * `createGitHubOctokit`.
10
7
  *
11
- * Credentials resolve from options first, then the environment:
12
- * `ORIGIN_APP_ID` (or `AGENT_SERVE_ORIGIN_APP_ID`),
13
- * `ORIGIN_APP_INSTALLATION_ID`, `ORIGIN_APP_PRIVATE_KEY` (PKCS8 PEM for the
14
- * app's Ed25519 key; literal `\n` sequences are normalized), and
15
- * `ORIGIN_API_BASE_URL` for the public REST base.
8
+ * Self-configured channels with their own Origin app resolve credentials
9
+ * from options first, then the environment: `ORIGIN_APP_ID` (or
10
+ * `AGENT_SERVE_ORIGIN_APP_ID`), `ORIGIN_APP_INSTALLATION_ID`,
11
+ * `ORIGIN_APP_PRIVATE_KEY` (PKCS8 PEM for the app's Ed25519 key; literal
12
+ * `\n` sequences are normalized), and `ORIGIN_API_BASE_URL` for the public
13
+ * REST base.
14
+ *
15
+ * Hosted v2 forks configure none of that: their only credential is the
16
+ * deployment service account's `CURSOR_API_KEY`, exchanged for a Cursor
17
+ * session ({@link cursorSessionTokenProvider}, `POST
18
+ * /auth/exchange_user_api_key`) and sent as the Origin bearer. Reads and
19
+ * writes both run as the deployment **service-account principal**; Origin's
20
+ * `agent-serve` allowlist grants team-namespace read/write, so there is no
21
+ * app-installation token exchange for managed deployments.
16
22
  */
17
23
 
18
24
  import { Buffer } from "node:buffer";
19
25
  import { createPrivateKey, sign } from "node:crypto";
26
+ import {
27
+ cursorBackendUrl,
28
+ resolveApiKeySync,
29
+ } from "../../internal/cursor/credentials.js";
30
+ import {
31
+ CURSOR_SESSION_EXCHANGE_PATH,
32
+ exchangeCursorApiKeyForSession,
33
+ SESSION_TOKEN_EXPIRY_LEEWAY_MS,
34
+ } from "../../internal/cursor/session-token.js";
20
35
 
21
36
  /**
22
37
  * Public Origin REST base (check runs, installation tokens). Distinct from
@@ -58,7 +73,10 @@ export class OriginApiError extends Error {
58
73
  }
59
74
  }
60
75
 
61
- /** Yields a bearer for Origin REST calls (usually a short-lived `oit_…`). */
76
+ /**
77
+ * Yields a bearer for Origin REST calls (a short-lived `oit_…` for app
78
+ * channels, or a Cursor session token for service-account access).
79
+ */
62
80
  export type OriginTokenProvider = () => Promise<string>;
63
81
 
64
82
  export interface OriginAppCredentials {
@@ -181,6 +199,147 @@ async function mintOriginInstallationToken(input: {
181
199
  return token;
182
200
  }
183
201
 
202
+ /**
203
+ * Exchange endpoint for Cursor-account session tokens. Override with
204
+ * `AGENT_SERVE_CURSOR_SESSION_EXCHANGE_URL`; defaults to the Cursor backend.
205
+ */
206
+ export function resolveCursorSessionExchangeUrl(): string {
207
+ const override = process.env.AGENT_SERVE_CURSOR_SESSION_EXCHANGE_URL;
208
+ if (override !== undefined && override.trim() !== "") {
209
+ return override.trim().replace(/\/+$/, "");
210
+ }
211
+ return `${cursorBackendUrl()}${CURSOR_SESSION_EXCHANGE_PATH}`;
212
+ }
213
+
214
+ /**
215
+ * A session-backed Origin token provider: {@link OriginTokenProvider} plus
216
+ * an `invalidate` hook `createOriginClient` calls when Origin rejects the
217
+ * bearer (401), so the next mint re-exchanges instead of serving the
218
+ * rejected session until its local renewal deadline.
219
+ */
220
+ export type CursorSessionTokenProvider = OriginTokenProvider & {
221
+ invalidate: (token: string) => void;
222
+ };
223
+
224
+ /**
225
+ * Token provider backed by the shared Cursor session exchange
226
+ * (`exchangeCursorApiKeyForSession`, `POST /auth/exchange_user_api_key` —
227
+ * the same primitive `CursorBackendClient` mints through).
228
+ *
229
+ * Hosted v2 forks hold exactly one credential — the deployment service
230
+ * account's `CURSOR_API_KEY` — and Origin authenticates the exchanged
231
+ * session directly: reads and writes run as the deployment
232
+ * **service-account principal**, confined server-side to namespaces owned
233
+ * by the deployment's team (`agent-serve` allowlist). Local runs work the
234
+ * same way with a personal key: Origin authorizes the user principal.
235
+ *
236
+ * The API key is re-read on every mint so Bind can drop
237
+ * `CURSOR_API_KEY_FILE` onto a listening process; sessions are cached until
238
+ * shortly before their JWT expiry. The cache is **process-wide**, keyed by
239
+ * (endpoint, API key): hosted channels build a fresh `createOriginClient()`
240
+ * per event, so a per-provider cache would re-exchange on every read/write
241
+ * and concurrent first calls would race. In-flight exchanges are deduped
242
+ * for the same reason.
243
+ */
244
+ const cursorSessionTokenCache = new Map<
245
+ string,
246
+ { token: string; renewAtMs: number }
247
+ >();
248
+ const cursorSessionTokenInFlight = new Map<string, Promise<string>>();
249
+
250
+ /** Test-only: drop the process-wide exchanged-session cache. */
251
+ export function _clearCursorSessionTokenCacheForTesting(): void {
252
+ cursorSessionTokenCache.clear();
253
+ cursorSessionTokenInFlight.clear();
254
+ }
255
+
256
+ export function cursorSessionTokenProvider(input?: {
257
+ /** Cursor API key source. Defaults to the standard host resolution. */
258
+ getApiKey?: () => string | undefined;
259
+ /** Exchange endpoint. Defaults to {@link resolveCursorSessionExchangeUrl}. */
260
+ exchangeUrl?: string;
261
+ fetch?: typeof fetch;
262
+ now?: () => number;
263
+ }): CursorSessionTokenProvider {
264
+ const getApiKey =
265
+ input?.getApiKey ?? ((): string | undefined => resolveApiKeySync()?.apiKey);
266
+ const fetchImpl = input?.fetch ?? fetch;
267
+ const now = input?.now ?? Date.now;
268
+
269
+ const cacheKey = (url: string, apiKey: string): string =>
270
+ `${url}\u0000${apiKey}`;
271
+ const resolveUrl = (): string =>
272
+ input?.exchangeUrl ?? resolveCursorSessionExchangeUrl();
273
+
274
+ const mint = async (url: string, apiKey: string): Promise<string> => {
275
+ const nowMs = now();
276
+ let session: { token: string; expiresAtMs: number };
277
+ try {
278
+ session = await exchangeCursorApiKeyForSession({
279
+ exchangeUrl: url,
280
+ apiKey,
281
+ fetchImpl,
282
+ nowMs,
283
+ });
284
+ } catch (error) {
285
+ throw new Error(
286
+ `originClient: ${
287
+ error instanceof Error ? error.message : String(error)
288
+ } Check CURSOR_API_KEY / CURSOR_API_KEY_FILE, or pass an explicit token.`
289
+ );
290
+ }
291
+ const renewAtMs = Math.max(
292
+ nowMs + 1_000,
293
+ session.expiresAtMs - SESSION_TOKEN_EXPIRY_LEEWAY_MS
294
+ );
295
+ cursorSessionTokenCache.set(cacheKey(url, apiKey), {
296
+ token: session.token,
297
+ renewAtMs,
298
+ });
299
+ return session.token;
300
+ };
301
+
302
+ const provider = async (): Promise<string> => {
303
+ const apiKey = getApiKey();
304
+ if (apiKey === undefined || apiKey === "") {
305
+ throw new Error(
306
+ "originClient: no Cursor credential available for the session exchange (set CURSOR_API_KEY or CURSOR_API_KEY_FILE)."
307
+ );
308
+ }
309
+ const url = resolveUrl();
310
+ const key = cacheKey(url, apiKey);
311
+ const cached = cursorSessionTokenCache.get(key);
312
+ if (cached !== undefined && now() < cached.renewAtMs) {
313
+ return cached.token;
314
+ }
315
+ // Dedupe concurrent first calls: one exchange per (endpoint, key).
316
+ const inFlight = cursorSessionTokenInFlight.get(key);
317
+ if (inFlight !== undefined) {
318
+ return await inFlight;
319
+ }
320
+ const pending = mint(url, apiKey).finally(() => {
321
+ cursorSessionTokenInFlight.delete(key);
322
+ });
323
+ cursorSessionTokenInFlight.set(key, pending);
324
+ return await pending;
325
+ };
326
+
327
+ // Origin answered 401 for this bearer: drop it (unless a newer session
328
+ // already replaced it) so the next call re-exchanges immediately.
329
+ const invalidate = (token: string): void => {
330
+ const apiKey = getApiKey();
331
+ if (apiKey === undefined || apiKey === "") {
332
+ return;
333
+ }
334
+ const key = cacheKey(resolveUrl(), apiKey);
335
+ if (cursorSessionTokenCache.get(key)?.token === token) {
336
+ cursorSessionTokenCache.delete(key);
337
+ }
338
+ };
339
+
340
+ return Object.assign(provider, { invalidate });
341
+ }
342
+
184
343
  /**
185
344
  * A cached per-use token provider for one set of app credentials. Every call
186
345
  * inside the reuse window returns the same `oit_…`; the first call after it
@@ -224,6 +383,18 @@ export interface OriginClientOptions {
224
383
  apiBaseUrl?: string;
225
384
  /** Injectable fetch for tests. */
226
385
  fetch?: typeof fetch;
386
+ /**
387
+ * Cursor-account session exchange fallback (hosted v2 deployment SAs and
388
+ * local personal keys). `true` (default) engages when no explicit token
389
+ * or app credentials resolve and a Cursor credential is present; `false`
390
+ * disables it; an object injects the key source / endpoint.
391
+ */
392
+ cursorAccountExchange?:
393
+ | boolean
394
+ | {
395
+ getApiKey?: () => string | undefined;
396
+ exchangeUrl?: string;
397
+ };
227
398
  }
228
399
 
229
400
  /** Minimal authenticated Origin REST client (the `Octokit` analogue). */
@@ -242,8 +413,11 @@ export interface OriginClient {
242
413
 
243
414
  /**
244
415
  * Build an authenticated Origin REST client. Auth resolution: explicit
245
- * token/provider, then app credentials (options or environment). Missing
246
- * auth fails at the first request with a clear message.
416
+ * token/provider, then app credentials (options or environment, for
417
+ * channels with their own Origin app), then the Cursor-account session
418
+ * exchange (hosted v2 deployment SAs — reads and writes both run as the
419
+ * deployment service-account principal). Missing auth fails at the first
420
+ * request with a clear message.
247
421
  */
248
422
  export function createOriginClient(
249
423
  options: OriginClientOptions = {}
@@ -255,6 +429,10 @@ export function createOriginClient(
255
429
  const fetchImpl = options.fetch ?? fetch;
256
430
 
257
431
  let tokenProvider: OriginTokenProvider | undefined;
432
+ // Set when the session-exchange fallback is engaged: a 401 from Origin
433
+ // invalidates the cached session and retries once with a fresh one, the
434
+ // `CursorBackendClient` re-exchange behavior.
435
+ let sessionProvider: CursorSessionTokenProvider | undefined;
258
436
  const getToken = async (): Promise<string> => {
259
437
  if (typeof options.token === "string") {
260
438
  return options.token;
@@ -264,20 +442,55 @@ export function createOriginClient(
264
442
  }
265
443
  if (tokenProvider === undefined) {
266
444
  const credentials = options.credentials ?? resolveOriginAppCredentials();
267
- if (credentials === undefined) {
445
+ const exchange = options.cursorAccountExchange ?? true;
446
+ const exchangeOptions =
447
+ exchange === false ? undefined : exchange === true ? {} : exchange;
448
+ const getExchangeApiKey =
449
+ exchangeOptions?.getApiKey ??
450
+ ((): string | undefined => resolveApiKeySync()?.apiKey);
451
+ if (credentials !== undefined) {
452
+ tokenProvider = originAppTokenProvider({
453
+ credentials,
454
+ apiBaseUrl,
455
+ fetch: fetchImpl,
456
+ });
457
+ } else if (
458
+ exchangeOptions !== undefined &&
459
+ (getExchangeApiKey() ?? "") !== ""
460
+ ) {
461
+ sessionProvider = cursorSessionTokenProvider({
462
+ fetch: fetchImpl,
463
+ getApiKey: getExchangeApiKey,
464
+ ...(exchangeOptions.exchangeUrl === undefined
465
+ ? {}
466
+ : { exchangeUrl: exchangeOptions.exchangeUrl }),
467
+ });
468
+ tokenProvider = sessionProvider;
469
+ } else {
268
470
  throw new Error(
269
- "originClient: no Origin credential available (pass token / credentials, or set ORIGIN_APP_ID + ORIGIN_APP_INSTALLATION_ID + ORIGIN_APP_PRIVATE_KEY)."
471
+ "originClient: no Origin credential available (pass token / credentials, set ORIGIN_APP_ID + ORIGIN_APP_INSTALLATION_ID + ORIGIN_APP_PRIVATE_KEY, or sign in with a Cursor credential for the session exchange)."
270
472
  );
271
473
  }
272
- tokenProvider = originAppTokenProvider({
273
- credentials,
274
- apiBaseUrl,
275
- fetch: fetchImpl,
276
- });
277
474
  }
278
475
  return await tokenProvider();
279
476
  };
280
477
 
478
+ const send = async (
479
+ input: { method: OriginApiMethod; path: string; body?: unknown },
480
+ path: string,
481
+ token: string
482
+ ): Promise<Response> =>
483
+ await fetchImpl(`${apiBaseUrl}${path}`, {
484
+ method: input.method,
485
+ headers: {
486
+ authorization: `Bearer ${token}`,
487
+ ...(input.body === undefined
488
+ ? {}
489
+ : { "content-type": "application/json" }),
490
+ },
491
+ body: input.body === undefined ? undefined : JSON.stringify(input.body),
492
+ });
493
+
281
494
  return {
282
495
  apiBaseUrl,
283
496
  async request<T = unknown>(input: {
@@ -287,16 +500,15 @@ export function createOriginClient(
287
500
  }): Promise<T> {
288
501
  const path = input.path.startsWith("/") ? input.path : `/${input.path}`;
289
502
  const token = await getToken();
290
- const response = await fetchImpl(`${apiBaseUrl}${path}`, {
291
- method: input.method,
292
- headers: {
293
- authorization: `Bearer ${token}`,
294
- ...(input.body === undefined
295
- ? {}
296
- : { "content-type": "application/json" }),
297
- },
298
- body: input.body === undefined ? undefined : JSON.stringify(input.body),
299
- });
503
+ let response = await send(input, path, token);
504
+ if (response.status === 401 && sessionProvider !== undefined) {
505
+ // The session aged out server-side. Drop it — unless a concurrent
506
+ // retry already minted a newer one — and retry once.
507
+ await response.text().catch(() => undefined);
508
+ sessionProvider.invalidate(token);
509
+ const retryToken = await getToken();
510
+ response = await send(input, path, retryToken);
511
+ }
300
512
  const text = await response.text();
301
513
  let parsed: unknown;
302
514
  try {
@@ -38,8 +38,8 @@ export interface OriginHandle {
38
38
  }): Promise<T>;
39
39
  /**
40
40
  * Open a CI check run on `sha` and get a handle for updating and
41
- * concluding it. Needs an app installation with
42
- * `repository:checks:write` Origin check writes are installation-only.
41
+ * concluding it. Needs `repository:checks:write` — via an app
42
+ * installation, or the deployment service account on hosted v2 forks.
43
43
  */
44
44
  createCheck(
45
45
  input: Omit<OriginCheckCreateInput, "origin" | "owner" | "repo">
@@ -7,12 +7,12 @@
7
7
  * fresh `externalUpdatedAt`, and `resumeOriginCheck` needs only those
8
8
  * identity fields (no server id) to rebuild a handle after a restart.
9
9
  *
10
- * Posting needs `repository:checks:write` and is installation-only: the
11
- * client must authenticate with an `oit_…` installation token (see
12
- * `createOriginClient` / `originAppTokenProvider` in `api.js`). Works with
13
- * any client exposing `request()` (`ctx.origin`, `createOriginClient()`),
14
- * and is also reachable as `ctx.origin.createCheck(...)`. API failures throw
15
- * `OriginApiError`.
10
+ * Posting needs `repository:checks:write`: an `oit_…` installation token for
11
+ * app channels, or a deployment service-account session (hosted v2 forks —
12
+ * the `agent-serve` allowlist grants checks:write on team-owned namespaces).
13
+ * See `createOriginClient` in `api.js`. Works with any client exposing
14
+ * `request()` (`ctx.origin`, `createOriginClient()`), and is also reachable
15
+ * as `ctx.origin.createCheck(...)`. API failures throw `OriginApiError`.
16
16
  */
17
17
 
18
18
  import {
@@ -3,9 +3,11 @@
3
3
  *
4
4
  * Origin identity is the signed-in Cursor user (no third-party OAuth). The
5
5
  * channel declares repos at definition time; the serve host subscribes
6
- * `/v0/scm-events` with `provider: "origin"` as that user. Unlike GitHub,
7
- * there is no installation token to mint outbound Origin API calls already
8
- * authenticate as the Cursor principal.
6
+ * `/v0/scm-events` with `provider: "origin"` as that user. Outbound Origin
7
+ * API calls authenticate with the channel's app credentials when configured;
8
+ * hosted v2 deployments instead exchange the Cursor credential for a
9
+ * session (`cursorSessionTokenProvider` in ./api.ts), so reads and writes
10
+ * both run as the deployment service-account principal.
9
11
  */
10
12
 
11
13
  import type { OriginChannelOptions } from "./types.js";
@@ -26,6 +26,7 @@ import {
26
26
  import { deriveOriginWebhookEvents, ORIGIN_CHANNEL_KIND } from "./events.js";
27
27
  import {
28
28
  DEFAULT_ORIGIN_API_BASE_URL,
29
+ OriginJwksUnavailableError,
29
30
  type OriginWebhookHeaders,
30
31
  verifyOriginWebhook,
31
32
  } from "./origin-webhook.js";
@@ -300,10 +301,12 @@ export function parseOriginPullRequestEvent(input: {
300
301
  ctx: {
301
302
  deliveryId: input.envelope.deliveryId,
302
303
  eventType,
304
+ eventTime: input.envelope.event.eventTime,
303
305
  installationId: input.envelope.installationId,
304
306
  repository,
305
307
  pullRequestNumber: number,
306
308
  pullRequestUrl: url,
309
+ payload,
307
310
  sender: originSender(input.envelope, payload),
308
311
  },
309
312
  pullRequest: {
@@ -687,7 +690,16 @@ export async function handleOriginWebhookRequest(input: {
687
690
  await verifyOriginWebhook(bodyText, originHeaders(input.request), {
688
691
  apiBaseUrl: resolveApiBaseUrl(input.options),
689
692
  });
690
- } catch {
693
+ } catch (error) {
694
+ // A JWKS outage means the delivery was never checked: answer 503 so
695
+ // Origin's delivery worker retries. Only a real signature mismatch
696
+ // is the terminal 401.
697
+ if (error instanceof OriginJwksUnavailableError) {
698
+ return Response.json(
699
+ { ok: false, error: "signature_verification_unavailable" },
700
+ { status: 503 }
701
+ );
702
+ }
691
703
  return Response.json(
692
704
  { ok: false, error: "invalid_signature" },
693
705
  { status: 401 }
@@ -247,7 +247,31 @@ export interface VerifyOriginWebhookOptions {
247
247
  }
248
248
 
249
249
  /**
250
- * Verify a Standard-Webhooks v1ed Origin payload. Throws on failure.
250
+ * The signing JWKS could not be fetched (and no cached copy applies), so
251
+ * the delivery was never actually checked. Callers must answer retryably
252
+ * (5xx) — a 401 would make Origin's delivery worker drop the wake as
253
+ * terminal on what is only a transient outage.
254
+ */
255
+ export class OriginJwksUnavailableError extends Error {}
256
+
257
+ async function fetchJwksOrUnavailable(
258
+ apiBaseUrl: string,
259
+ fetchImpl: typeof fetch,
260
+ options?: FetchOriginJwksOptions
261
+ ): Promise<readonly OriginJwk[]> {
262
+ try {
263
+ return await fetchOriginJwks(apiBaseUrl, fetchImpl, options);
264
+ } catch (error) {
265
+ throw new OriginJwksUnavailableError(
266
+ `Origin JWKS unavailable: ${error instanceof Error ? error.message : String(error)}`
267
+ );
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Verify a Standard-Webhooks v1ed Origin payload. Throws
273
+ * {@link OriginJwksUnavailableError} when the keys cannot be fetched, and a
274
+ * plain error when the signature does not verify against them.
251
275
  */
252
276
  export async function verifyOriginWebhook(
253
277
  body: string | Buffer,
@@ -256,7 +280,7 @@ export async function verifyOriginWebhook(
256
280
  ): Promise<void> {
257
281
  const apiBaseUrl = options?.apiBaseUrl ?? DEFAULT_ORIGIN_API_BASE_URL;
258
282
  const fetchImpl = options?.fetchImpl ?? fetch;
259
- const keys = await fetchOriginJwks(apiBaseUrl, fetchImpl);
283
+ const keys = await fetchJwksOrUnavailable(apiBaseUrl, fetchImpl);
260
284
  if (
261
285
  verifyV1edWebhookPayload({
262
286
  publicJwks: keys,
@@ -267,7 +291,7 @@ export async function verifyOriginWebhook(
267
291
  ) {
268
292
  return;
269
293
  }
270
- const refreshed = await fetchOriginJwks(apiBaseUrl, fetchImpl, {
294
+ const refreshed = await fetchJwksOrUnavailable(apiBaseUrl, fetchImpl, {
271
295
  force: true,
272
296
  });
273
297
  if (
@@ -51,11 +51,19 @@ export interface OriginReview {
51
51
  export interface OriginEventContext {
52
52
  deliveryId?: string;
53
53
  eventType: string;
54
+ /** Delivery envelope `event.eventTime`, when present. */
55
+ eventTime?: string;
54
56
  /** Origin app installation id from the delivery envelope, when present. */
55
57
  installationId?: string;
56
58
  repository: OriginRepositoryRef;
57
59
  pullRequestNumber: number;
58
60
  pullRequestUrl: string;
61
+ /**
62
+ * Raw parsed `event.payload` record (`{}` when absent), for consumers
63
+ * whose normalization needs more than the typed event — the GitHub
64
+ * channel's hook ctx exposes the same.
65
+ */
66
+ payload: Record<string, unknown>;
59
67
  sender?: OriginActor;
60
68
  host: HostContext;
61
69
  artifacts: ArtifactsApi;
@@ -193,6 +193,12 @@ export function truncateTitle(title: string, max?: number): string {
193
193
  // Assistant / stream helpers
194
194
  // ---------------------------------------------------------------------------
195
195
 
196
+ /**
197
+ * Show or clear the Working chip. Agent UX ignores empty
198
+ * `assistant.threads.setStatus`; `agents.sessions.setStatus` `active` is
199
+ * what actually clears it. The assistant call stays for loading_messages
200
+ * and older workspaces.
201
+ */
196
202
  export async function setThreadStatus(args: {
197
203
  botToken: SlackBotToken | undefined;
198
204
  channelId: string;
@@ -204,13 +210,21 @@ export async function setThreadStatus(args: {
204
210
  return { ok: false, error: "missing_channel_or_thread" };
205
211
  }
206
212
  const status = truncateTypingStatus(args.status);
213
+ const sessionResult = await callSlackApiSoft({
214
+ botToken: args.botToken,
215
+ operation: "agents.sessions.setStatus",
216
+ body: {
217
+ channel_id: args.channelId,
218
+ thread_ts: args.threadTs,
219
+ status: status.length > 0 ? "processing" : "active",
220
+ },
221
+ });
222
+
207
223
  const body: Record<string, unknown> = {
208
224
  channel_id: args.channelId,
209
225
  thread_ts: args.threadTs,
210
226
  status,
211
227
  };
212
- // Empty status clears Slack's assistant chip ("App is working…").
213
- // Sending loading_messages with it keeps the chip rotating.
214
228
  if (status.length > 0) {
215
229
  const loadingRaw =
216
230
  args.loadingMessages !== undefined && args.loadingMessages.length > 0
@@ -224,11 +238,19 @@ export async function setThreadStatus(args: {
224
238
  body.loading_messages = loading_messages;
225
239
  }
226
240
  }
227
- return callSlackApiSoft({
241
+ const assistantResult = await callSlackApiSoft({
228
242
  botToken: args.botToken,
229
243
  operation: "assistant.threads.setStatus",
230
244
  body,
231
245
  });
246
+
247
+ if (sessionResult.ok) {
248
+ return sessionResult;
249
+ }
250
+ if (sessionResult.unsupported === true || assistantResult.ok) {
251
+ return assistantResult;
252
+ }
253
+ return sessionResult;
232
254
  }
233
255
 
234
256
  export async function setThreadTitle(args: {
@@ -278,16 +300,30 @@ export async function startChatStream(args: {
278
300
  channel: string;
279
301
  thread_ts?: string;
280
302
  markdown_text?: string;
303
+ chunks?: StreamTaskChunk[];
304
+ recipient_user_id?: string;
305
+ recipient_team_id?: string;
281
306
  }): Promise<SoftSlackResult<{ ts: string; channel: string }>> {
282
307
  const body: Record<string, unknown> = {
283
308
  channel: args.channel,
284
309
  };
310
+ // Empty markdown_text is rejected or opens a stream the assistant pane
311
+ // never attaches. Recipients are required outside DMs.
285
312
  if (args.thread_ts !== undefined && args.thread_ts !== "") {
286
313
  body.thread_ts = args.thread_ts;
287
314
  }
288
- if (args.markdown_text !== undefined) {
315
+ if (args.markdown_text !== undefined && args.markdown_text !== "") {
289
316
  body.markdown_text = args.markdown_text;
290
317
  }
318
+ if (args.chunks !== undefined && args.chunks.length > 0) {
319
+ body.chunks = args.chunks;
320
+ }
321
+ if (args.recipient_user_id !== undefined && args.recipient_user_id !== "") {
322
+ body.recipient_user_id = args.recipient_user_id;
323
+ }
324
+ if (args.recipient_team_id !== undefined && args.recipient_team_id !== "") {
325
+ body.recipient_team_id = args.recipient_team_id;
326
+ }
291
327
  const result = await callSlackApiSoft({
292
328
  botToken: args.botToken,
293
329
  operation: "chat.startStream",
@@ -642,7 +678,7 @@ export function buildSlackBinding(args: {
642
678
  ...(loadingMessages === undefined ? {} : { loadingMessages }),
643
679
  });
644
680
  if (!result.ok) {
645
- log.warn("assistant.threads.setStatus not-ok", {
681
+ log.warn("slack setStatus not-ok", {
646
682
  error: result.error,
647
683
  channel: args.channelId,
648
684
  thread_ts: threadTs,