@cursor/july 0.1.62 → 0.1.73

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 (286) hide show
  1. package/AGENTS.md +4 -5
  2. package/README.md +23 -11
  3. package/dist/bin/agent-serve.js +11 -2
  4. package/dist/channels/github/api.d.ts +2 -0
  5. package/dist/channels/github/api.d.ts.map +1 -1
  6. package/dist/channels/github/api.js +2 -1
  7. package/dist/channels/github/binding.d.ts +7 -0
  8. package/dist/channels/github/binding.d.ts.map +1 -1
  9. package/dist/channels/github/binding.js +4 -0
  10. package/dist/channels/github/checks.d.ts +99 -0
  11. package/dist/channels/github/checks.d.ts.map +1 -0
  12. package/dist/channels/github/checks.js +108 -0
  13. package/dist/channels/github/cursor-account.d.ts +4 -4
  14. package/dist/channels/github/cursor-account.d.ts.map +1 -1
  15. package/dist/channels/github/cursor-account.js +7 -5
  16. package/dist/channels/github/index.d.ts +1 -0
  17. package/dist/channels/github/index.d.ts.map +1 -1
  18. package/dist/channels/github/index.js +1 -0
  19. package/dist/channels/github/types.d.ts +8 -0
  20. package/dist/channels/github/types.d.ts.map +1 -1
  21. package/dist/channels/slack/channel-watch.d.ts +2 -1
  22. package/dist/channels/slack/channel-watch.d.ts.map +1 -1
  23. package/dist/channels/slack/channel-watch.js +7 -3
  24. package/dist/channels/slack/dispatch.d.ts +9 -0
  25. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  26. package/dist/channels/slack/dispatch.js +41 -21
  27. package/dist/channels/slack/manifest.d.ts +2 -2
  28. package/dist/channels/slack/manifest.d.ts.map +1 -1
  29. package/dist/channels/slack/manifest.js +14 -11
  30. package/dist/channels/slack/nudge-store.d.ts +16 -0
  31. package/dist/channels/slack/nudge-store.d.ts.map +1 -1
  32. package/dist/channels/slack/nudge-store.js +53 -0
  33. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  34. package/dist/channels/slack/slack-channel.js +4 -1
  35. package/dist/channels/slack/types.d.ts +24 -7
  36. package/dist/channels/slack/types.d.ts.map +1 -1
  37. package/dist/connections.d.ts +17 -5
  38. package/dist/connections.d.ts.map +1 -1
  39. package/dist/connections.js +17 -5
  40. package/dist/docs/404.html +2 -2
  41. package/dist/docs/ab.html +3 -3
  42. package/dist/docs/assets/{app.B-biogkN.js → app.CdbcFD8T.js} +1 -1
  43. package/dist/docs/assets/chunks/@localSearchIndexroot.DQad--_z.js +1 -0
  44. package/dist/docs/assets/chunks/{VPLocalSearchBox.DZqKtFZS.js → VPLocalSearchBox.D_NKImM7.js} +1 -1
  45. package/dist/docs/assets/chunks/{theme.BwC5zP2r.js → theme.CMvz7cu9.js} +2 -2
  46. package/dist/docs/assets/{concepts.md.CqOsxbMU.js → concepts.md.BXAm6G-C.js} +1 -1
  47. package/dist/docs/assets/{guides_mcp-oauth.md.C7G7IykG.js → guides_mcp-oauth.md.CzEB6RaG.js} +1 -1
  48. package/dist/docs/assets/{reference_agent-config.md.DHkkvcEZ.js → reference_agent-config.md.BZnEKfEc.js} +3 -4
  49. package/dist/docs/assets/{reference_agent-config.md.DHkkvcEZ.lean.js → reference_agent-config.md.BZnEKfEc.lean.js} +1 -1
  50. package/dist/docs/assets/{reference_connections.md.CSQiNzre.js → reference_connections.md.Cu3N-S3Q.js} +26 -9
  51. package/dist/docs/assets/reference_connections.md.Cu3N-S3Q.lean.js +1 -0
  52. package/dist/docs/assets/{reference_sessions.md.DBVFi2Sx.js → reference_sessions.md.B0DdlM-K.js} +1 -1
  53. package/dist/docs/assets/{reference_tools.md.kJfJAP2m.js → reference_tools.md.DTg_kEsx.js} +2 -2
  54. package/dist/docs/assets/{reference_tools.md.kJfJAP2m.lean.js → reference_tools.md.DTg_kEsx.lean.js} +1 -1
  55. package/dist/docs/assets/troubleshooting.md.DQ-hYuZT.js +1 -0
  56. package/dist/docs/building-with-agents.html +3 -3
  57. package/dist/docs/concepts.html +5 -5
  58. package/dist/docs/deployment.html +3 -3
  59. package/dist/docs/evals.html +3 -3
  60. package/dist/docs/example-agents/approval-buddy.html +3 -3
  61. package/dist/docs/example-agents/benny.html +3 -3
  62. package/dist/docs/example-agents/bugbot.html +3 -3
  63. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  64. package/dist/docs/example-agents/codeowners-review.html +3 -3
  65. package/dist/docs/example-agents/concierge.html +3 -3
  66. package/dist/docs/example-agents/fsd.html +3 -3
  67. package/dist/docs/example-agents/index.html +3 -3
  68. package/dist/docs/example-agents/knowledge-base.html +3 -3
  69. package/dist/docs/example-agents/oncall.html +3 -3
  70. package/dist/docs/example-agents/security-reviewer.html +3 -3
  71. package/dist/docs/example-agents/slack-agent.html +3 -3
  72. package/dist/docs/example-agents/weather-agent.html +3 -3
  73. package/dist/docs/guides/agent-to-agent.html +3 -3
  74. package/dist/docs/guides/cloud-runtime.html +3 -3
  75. package/dist/docs/guides/convert-automation.html +3 -3
  76. package/dist/docs/guides/github.html +3 -3
  77. package/dist/docs/guides/human-in-the-loop.html +3 -3
  78. package/dist/docs/guides/mcp-oauth.html +5 -5
  79. package/dist/docs/guides/opentelemetry.html +3 -3
  80. package/dist/docs/guides/slack.html +3 -3
  81. package/dist/docs/guides/webhooks.html +3 -3
  82. package/dist/docs/hashmap.json +1 -1
  83. package/dist/docs/hillclimbing.html +3 -3
  84. package/dist/docs/index.html +3 -3
  85. package/dist/docs/quickstart.html +3 -3
  86. package/dist/docs/reference/agent-config.html +5 -6
  87. package/dist/docs/reference/artifacts.html +3 -3
  88. package/dist/docs/reference/channels.html +3 -3
  89. package/dist/docs/reference/cli.html +3 -3
  90. package/dist/docs/reference/connections.html +28 -11
  91. package/dist/docs/reference/hooks.html +3 -3
  92. package/dist/docs/reference/http-api.html +3 -3
  93. package/dist/docs/reference/instructions.html +3 -3
  94. package/dist/docs/reference/playground.html +3 -3
  95. package/dist/docs/reference/project-layout.html +3 -3
  96. package/dist/docs/reference/prompt.html +3 -3
  97. package/dist/docs/reference/schedules.html +3 -3
  98. package/dist/docs/reference/sessions.html +5 -5
  99. package/dist/docs/reference/skills.html +3 -3
  100. package/dist/docs/reference/subagents.html +3 -3
  101. package/dist/docs/reference/tools.html +5 -5
  102. package/dist/docs/scaffolding-agents.html +3 -3
  103. package/dist/docs/storage.html +3 -3
  104. package/dist/docs/templates/agentic-owners.html +3 -3
  105. package/dist/docs/templates/demo.html +3 -3
  106. package/dist/docs/templates/pr-autofixer.html +3 -3
  107. package/dist/docs/templates/security-reviewer.html +3 -3
  108. package/dist/docs/templates/triage.html +3 -3
  109. package/dist/docs/troubleshooting.html +5 -5
  110. package/dist/files-backends/agent-store-presigned-url.js +1 -1
  111. package/dist/index.d.ts +2 -2
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +1 -1
  114. package/dist/internal/advertise-tools.d.ts +79 -0
  115. package/dist/internal/advertise-tools.d.ts.map +1 -0
  116. package/dist/internal/advertise-tools.js +185 -0
  117. package/dist/internal/cli-deploy.d.ts.map +1 -1
  118. package/dist/internal/cli-deploy.js +7 -1
  119. package/dist/internal/cli-manifest.d.ts +19 -0
  120. package/dist/internal/cli-manifest.d.ts.map +1 -0
  121. package/dist/internal/cli-manifest.js +49 -0
  122. package/dist/internal/cli-slack.d.ts.map +1 -1
  123. package/dist/internal/cli-slack.js +9 -1
  124. package/dist/internal/convert-automation/convert-workflow.d.ts.map +1 -1
  125. package/dist/internal/convert-automation/convert-workflow.js +2 -10
  126. package/dist/internal/cursor/account-mcp.d.ts +29 -3
  127. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  128. package/dist/internal/cursor/account-mcp.js +147 -67
  129. package/dist/internal/cursor/backend-client.d.ts +11 -6
  130. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  131. package/dist/internal/cursor/backend-client.js +32 -10
  132. package/dist/internal/cursor-account-mcp-auth.d.ts +11 -0
  133. package/dist/internal/cursor-account-mcp-auth.d.ts.map +1 -1
  134. package/dist/internal/cursor-account-mcp-auth.js +69 -0
  135. package/dist/internal/cursor-agent-template.d.ts +9 -0
  136. package/dist/internal/cursor-agent-template.d.ts.map +1 -0
  137. package/dist/internal/cursor-agent-template.js +37 -0
  138. package/dist/internal/deploy-client.d.ts +1 -0
  139. package/dist/internal/deploy-client.d.ts.map +1 -1
  140. package/dist/internal/deploy-client.js +18 -2
  141. package/dist/internal/deploy-manifest.d.ts +76 -0
  142. package/dist/internal/deploy-manifest.d.ts.map +1 -0
  143. package/dist/internal/deploy-manifest.js +117 -0
  144. package/dist/internal/deployment-scm-event-stream.d.ts +116 -0
  145. package/dist/internal/deployment-scm-event-stream.d.ts.map +1 -0
  146. package/dist/internal/deployment-scm-event-stream.js +474 -0
  147. package/dist/internal/discovery.d.ts.map +1 -1
  148. package/dist/internal/discovery.js +111 -10
  149. package/dist/internal/init-project.d.ts.map +1 -1
  150. package/dist/internal/init-project.js +12 -123
  151. package/dist/internal/init-scaffold.d.ts +8 -0
  152. package/dist/internal/init-scaffold.d.ts.map +1 -0
  153. package/dist/internal/init-scaffold.js +168 -0
  154. package/dist/internal/inline-stdio-mcp.d.ts +2 -0
  155. package/dist/internal/inline-stdio-mcp.d.ts.map +1 -1
  156. package/dist/internal/inline-stdio-mcp.js +10 -8
  157. package/dist/internal/json-schema.d.ts +7 -0
  158. package/dist/internal/json-schema.d.ts.map +1 -1
  159. package/dist/internal/json-schema.js +18 -3
  160. package/dist/internal/mcp-endpoint.js +7 -7
  161. package/dist/internal/mcp-host.d.ts +18 -0
  162. package/dist/internal/mcp-host.d.ts.map +1 -1
  163. package/dist/internal/mcp-host.js +43 -5
  164. package/dist/internal/mcp-oauth.d.ts +22 -0
  165. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  166. package/dist/internal/mcp-oauth.js +40 -1
  167. package/dist/internal/package-inline-stdio.d.ts.map +1 -1
  168. package/dist/internal/package-inline-stdio.js +5 -5
  169. package/dist/internal/review-comments.d.ts +97 -0
  170. package/dist/internal/review-comments.d.ts.map +1 -1
  171. package/dist/internal/review-comments.js +151 -0
  172. package/dist/internal/sanitize-mcp-tool-name.d.ts +20 -0
  173. package/dist/internal/sanitize-mcp-tool-name.d.ts.map +1 -0
  174. package/dist/internal/sanitize-mcp-tool-name.js +30 -0
  175. package/dist/internal/sdk-runner.d.ts +1 -1
  176. package/dist/internal/sdk-runner.d.ts.map +1 -1
  177. package/dist/internal/sdk-runner.js +36 -13
  178. package/dist/internal/server.d.ts.map +1 -1
  179. package/dist/internal/server.js +18 -0
  180. package/dist/internal/session-engine.d.ts +66 -0
  181. package/dist/internal/session-engine.d.ts.map +1 -1
  182. package/dist/internal/session-engine.js +428 -110
  183. package/dist/internal/shallow-clone.d.ts +170 -0
  184. package/dist/internal/shallow-clone.d.ts.map +1 -0
  185. package/dist/internal/shallow-clone.js +412 -0
  186. package/dist/internal/slack-provision-client.d.ts +2 -0
  187. package/dist/internal/slack-provision-client.d.ts.map +1 -1
  188. package/dist/internal/slack-provision-client.js +1 -0
  189. package/dist/internal/terminal-style.d.ts +18 -0
  190. package/dist/internal/terminal-style.d.ts.map +1 -1
  191. package/dist/internal/terminal-style.js +46 -1
  192. package/dist/internal/turn-lease-store.d.ts +44 -0
  193. package/dist/internal/turn-lease-store.d.ts.map +1 -0
  194. package/dist/internal/turn-lease-store.js +139 -0
  195. package/dist/internal/workspace.d.ts +14 -0
  196. package/dist/internal/workspace.d.ts.map +1 -1
  197. package/dist/internal/workspace.js +33 -0
  198. package/dist/playground/assets/index-77uhu5Kr.css +1 -0
  199. package/dist/playground/assets/index-D4buox4m.js +85 -0
  200. package/dist/playground/index.html +2 -2
  201. package/dist/storage-protocol.d.ts +122 -0
  202. package/dist/storage-protocol.d.ts.map +1 -0
  203. package/dist/storage-protocol.js +173 -0
  204. package/dist/storage.d.ts +2 -72
  205. package/dist/storage.d.ts.map +1 -1
  206. package/dist/storage.js +2 -82
  207. package/dist/tools.d.ts +7 -0
  208. package/dist/tools.d.ts.map +1 -1
  209. package/dist/types.d.ts +141 -6
  210. package/dist/types.d.ts.map +1 -1
  211. package/docs/concepts.md +4 -4
  212. package/docs/guides/mcp-oauth.md +3 -1
  213. package/docs/reference/agent-config.md +11 -12
  214. package/docs/reference/connections.md +108 -10
  215. package/docs/reference/sessions.md +2 -2
  216. package/docs/reference/tools.md +14 -0
  217. package/docs/troubleshooting.md +3 -2
  218. package/package.json +7 -2
  219. package/skills/create-agent/SKILL.md +12 -9
  220. package/skills/debug/SKILL.md +2 -1
  221. package/skills/framework-map/SKILL.md +6 -6
  222. package/skills/mcp-auth/SKILL.md +8 -6
  223. package/src/bin/agent-serve.ts +12 -2
  224. package/src/channels/github/api.ts +6 -1
  225. package/src/channels/github/binding.ts +21 -0
  226. package/src/channels/github/checks.ts +179 -0
  227. package/src/channels/github/cursor-account.ts +9 -6
  228. package/src/channels/github/index.ts +1 -0
  229. package/src/channels/github/types.ts +8 -0
  230. package/src/channels/slack/channel-watch.ts +6 -3
  231. package/src/channels/slack/dispatch.ts +40 -3
  232. package/src/channels/slack/manifest.ts +9 -10
  233. package/src/channels/slack/nudge-store.ts +56 -0
  234. package/src/channels/slack/slack-channel.ts +3 -0
  235. package/src/channels/slack/types.ts +24 -7
  236. package/src/connections.ts +17 -5
  237. package/src/index.ts +5 -0
  238. package/src/internal/advertise-tools.ts +278 -0
  239. package/src/internal/cli-deploy.ts +11 -1
  240. package/src/internal/cli-manifest.ts +63 -0
  241. package/src/internal/cli-slack.ts +10 -1
  242. package/src/internal/convert-automation/convert-workflow.ts +2 -11
  243. package/src/internal/cursor/account-mcp.ts +208 -73
  244. package/src/internal/cursor/backend-client.ts +45 -11
  245. package/src/internal/cursor-account-mcp-auth.ts +83 -0
  246. package/src/internal/cursor-agent-template.ts +46 -0
  247. package/src/internal/deploy-client.ts +25 -6
  248. package/src/internal/deploy-manifest.ts +161 -0
  249. package/src/internal/deployment-scm-event-stream.ts +593 -0
  250. package/src/internal/discovery.ts +132 -3
  251. package/src/internal/init-project.ts +18 -153
  252. package/src/internal/init-scaffold.ts +176 -0
  253. package/src/internal/inline-stdio-mcp.ts +13 -0
  254. package/src/internal/json-schema.ts +24 -3
  255. package/src/internal/mcp-endpoint.ts +6 -0
  256. package/src/internal/mcp-host.ts +70 -9
  257. package/src/internal/mcp-oauth.ts +55 -1
  258. package/src/internal/package-inline-stdio.ts +5 -1
  259. package/src/internal/review-comments.ts +245 -0
  260. package/src/internal/sanitize-mcp-tool-name.ts +33 -0
  261. package/src/internal/sdk-runner.ts +50 -11
  262. package/src/internal/server.ts +19 -0
  263. package/src/internal/session-engine.ts +392 -19
  264. package/src/internal/shallow-clone.ts +595 -0
  265. package/src/internal/slack-provision-client.ts +3 -0
  266. package/src/internal/terminal-style.ts +67 -1
  267. package/src/internal/turn-lease-store.ts +147 -0
  268. package/src/internal/workspace.ts +33 -0
  269. package/src/storage-protocol.ts +247 -0
  270. package/src/storage.ts +16 -110
  271. package/src/tools.ts +7 -0
  272. package/src/types.ts +149 -7
  273. package/templates/agentic-owners/agent/agent.ts +3 -0
  274. package/templates/demo/agent/agent.ts +3 -0
  275. package/templates/pr-autofixer/agent/lib/drive-schemas.ts +4 -1
  276. package/templates/security-reviewer/agent/agent.ts +3 -0
  277. package/templates/triage/agent/agent.ts +3 -0
  278. package/dist/docs/assets/chunks/@localSearchIndexroot.5ozYNf74.js +0 -1
  279. package/dist/docs/assets/reference_connections.md.CSQiNzre.lean.js +0 -1
  280. package/dist/docs/assets/troubleshooting.md.CuVztCKs.js +0 -1
  281. package/dist/playground/assets/index-B4rcOgeP.css +0 -1
  282. package/dist/playground/assets/index-DXcY42Jv.js +0 -85
  283. /package/dist/docs/assets/{concepts.md.CqOsxbMU.lean.js → concepts.md.BXAm6G-C.lean.js} +0 -0
  284. /package/dist/docs/assets/{guides_mcp-oauth.md.C7G7IykG.lean.js → guides_mcp-oauth.md.CzEB6RaG.lean.js} +0 -0
  285. /package/dist/docs/assets/{reference_sessions.md.DBVFi2Sx.lean.js → reference_sessions.md.B0DdlM-K.lean.js} +0 -0
  286. /package/dist/docs/assets/{troubleshooting.md.CuVztCKs.lean.js → troubleshooting.md.DQ-hYuZT.lean.js} +0 -0
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Generic CI checks (GitHub Checks API) for agent authors.
3
+ *
4
+ * `createGitHubCheck` opens a merge-box check run on a commit and returns a
5
+ * handle for updating and concluding it, so an agent can report its own CI
6
+ * result (name, summary, and a "Details" link — e.g. the agent session run).
7
+ * Works with any client exposing `getOctokit()` (`ctx.github`,
8
+ * `ctx.host.github`), and is also reachable as `ctx.github.createCheck(...)`.
9
+ *
10
+ * Posting checks needs `checks:write`. With `cursorAccount`, declare
11
+ * `githubChannel({ checks: true })` so the hosted lease is minted on the
12
+ * `contents-write` tier; GitHub App / PAT credentials need the equivalent
13
+ * permission.
14
+ */
15
+
16
+ import type { Octokit } from "@octokit/rest";
17
+ import {
18
+ type GitHubCheckRunConclusion,
19
+ type GitHubCheckRunStatus,
20
+ wrapOctokitError,
21
+ } from "./api.js";
22
+
23
+ /** Any client that can hand out an authenticated Octokit. */
24
+ export interface GitHubCheckClient {
25
+ getOctokit(): Promise<Octokit>;
26
+ }
27
+
28
+ /** Output shown on the check page (both fields required by GitHub). */
29
+ export interface GitHubCheckOutput {
30
+ /** Heading on the check page. */
31
+ title: string;
32
+ /** Markdown body (supports links, e.g. `[View session](…)`). */
33
+ summary: string;
34
+ }
35
+
36
+ export interface GitHubCheckCreateInput {
37
+ /** Client used for every API call on the returned handle. */
38
+ github: GitHubCheckClient;
39
+ /** Repository owner login, e.g. `"acme"`. */
40
+ owner: string;
41
+ /** Repository name, e.g. `"widgets"`. */
42
+ repo: string;
43
+ /** Check name shown in the PR merge box, e.g. `"Cursor Security Review"`. */
44
+ name: string;
45
+ /** Commit the check attaches to (usually the PR head SHA). */
46
+ sha: string;
47
+ /** Initial status. Defaults to `"in_progress"`. */
48
+ status?: "queued" | "in_progress";
49
+ /** "Details" link on the check, e.g. the agent session run URL. */
50
+ detailsUrl?: string;
51
+ /** Title + markdown summary shown on the check page. */
52
+ output?: GitHubCheckOutput;
53
+ }
54
+
55
+ export interface GitHubCheckUpdateInput {
56
+ status?: GitHubCheckRunStatus;
57
+ conclusion?: GitHubCheckRunConclusion;
58
+ detailsUrl?: string;
59
+ output?: GitHubCheckOutput;
60
+ }
61
+
62
+ export interface GitHubCheckConcludeInput {
63
+ /** Final result shown in the merge box. */
64
+ conclusion: GitHubCheckRunConclusion;
65
+ detailsUrl?: string;
66
+ output?: GitHubCheckOutput;
67
+ }
68
+
69
+ /** A created check run. All methods throw `GitHubApiError` on API failure. */
70
+ export interface GitHubCheck {
71
+ /** Check run id (for reuse across process restarts). */
72
+ readonly id: number;
73
+ /** GitHub web URL of the check run page, when returned by the API. */
74
+ readonly url: string | undefined;
75
+ /** Patch status / output / details link while the check is running. */
76
+ update(input: GitHubCheckUpdateInput): Promise<void>;
77
+ /** Complete the check with a conclusion (sets `completed_at`). */
78
+ conclude(input: GitHubCheckConcludeInput): Promise<void>;
79
+ }
80
+
81
+ /**
82
+ * Create a CI check run on a commit and return a handle for later updates.
83
+ *
84
+ * ```ts
85
+ * const check = await createGitHubCheck({
86
+ * github: ctx.github,
87
+ * owner: ctx.repository.owner,
88
+ * repo: ctx.repository.name,
89
+ * name: "Cursor Security Review",
90
+ * sha: pr.headSha,
91
+ * detailsUrl: sessionUrl,
92
+ * output: { title: "Running…", summary: `[View session](${sessionUrl})` },
93
+ * });
94
+ * // …do the work…
95
+ * await check.conclude({ conclusion: "success", output: { … } });
96
+ * ```
97
+ */
98
+ export async function createGitHubCheck(
99
+ input: GitHubCheckCreateInput
100
+ ): Promise<GitHubCheck> {
101
+ const octokit = await input.github.getOctokit();
102
+ let created: { id: number; html_url?: string | null };
103
+ try {
104
+ const response = await octokit.rest.checks.create({
105
+ owner: input.owner,
106
+ repo: input.repo,
107
+ name: input.name,
108
+ head_sha: input.sha,
109
+ status: input.status ?? "in_progress",
110
+ started_at: new Date().toISOString(),
111
+ details_url: input.detailsUrl,
112
+ output: input.output,
113
+ });
114
+ created = response.data;
115
+ } catch (error) {
116
+ throw wrapOctokitError(
117
+ error,
118
+ "POST",
119
+ `/repos/${input.owner}/${input.repo}/check-runs`
120
+ );
121
+ }
122
+ return resumeGitHubCheck({
123
+ github: input.github,
124
+ owner: input.owner,
125
+ repo: input.repo,
126
+ name: input.name,
127
+ checkRunId: created.id,
128
+ url: typeof created.html_url === "string" ? created.html_url : undefined,
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Rebuild a {@link GitHubCheck} handle for an existing check run id (e.g.
134
+ * after a process restart). No API call is made until the first update.
135
+ */
136
+ export function resumeGitHubCheck(input: {
137
+ github: GitHubCheckClient;
138
+ owner: string;
139
+ repo: string;
140
+ name: string;
141
+ checkRunId: number;
142
+ url?: string;
143
+ }): GitHubCheck {
144
+ const patch = async (update: GitHubCheckUpdateInput): Promise<void> => {
145
+ const octokit = await input.github.getOctokit();
146
+ try {
147
+ await octokit.rest.checks.update({
148
+ owner: input.owner,
149
+ repo: input.repo,
150
+ check_run_id: input.checkRunId,
151
+ name: input.name,
152
+ status: update.status,
153
+ conclusion: update.conclusion,
154
+ completed_at:
155
+ update.status === "completed" ? new Date().toISOString() : undefined,
156
+ details_url: update.detailsUrl,
157
+ output: update.output,
158
+ });
159
+ } catch (error) {
160
+ throw wrapOctokitError(
161
+ error,
162
+ "PATCH",
163
+ `/repos/${input.owner}/${input.repo}/check-runs/${input.checkRunId}`
164
+ );
165
+ }
166
+ };
167
+ return {
168
+ id: input.checkRunId,
169
+ url: input.url,
170
+ update: patch,
171
+ conclude: (conclude) =>
172
+ patch({
173
+ status: "completed",
174
+ conclusion: conclude.conclusion,
175
+ detailsUrl: conclude.detailsUrl,
176
+ output: conclude.output,
177
+ }),
178
+ };
179
+ }
@@ -119,22 +119,25 @@ export function parseCursorGithubRepoScope(
119
119
 
120
120
  /**
121
121
  * Hosted `cursorAccount` mints `checks:write` on the `contents-write` tier.
122
- * Enabling `progress.commitStatus` opts a `pr-write` (or omitted) channel up
123
- * to that tier so github-proxy / `/github-token` can post Checks API runs.
124
- * `"read"` cannot post merge-box checks.
122
+ * Enabling `progress.commitStatus` or `checks: true` opts a `pr-write` (or
123
+ * omitted) channel up to that tier so github-proxy / `/github-token` can
124
+ * post Checks API runs. `"read"` cannot post merge-box checks.
125
125
  */
126
126
  export function cursorGithubPermissionsForChannel(
127
- options: Pick<GitHubChannelOptions, "progress">,
127
+ options: Pick<GitHubChannelOptions, "progress" | "checks">,
128
128
  authored: CursorGithubPermissions | undefined = "pr-write"
129
129
  ): CursorGithubPermissions {
130
130
  const base = authored ?? "pr-write";
131
131
  const commitStatus = options.progress?.commitStatus;
132
- if (commitStatus === undefined || commitStatus === false) {
132
+ const wantsChecks =
133
+ options.checks === true ||
134
+ (commitStatus !== undefined && commitStatus !== false);
135
+ if (!wantsChecks) {
133
136
  return base;
134
137
  }
135
138
  if (base === "read") {
136
139
  throw new Error(
137
- 'progress.commitStatus needs checks:write; cursorAccount permissions: "read" cannot post merge-box checks. Use "pr-write" or "contents-write".'
140
+ 'progress.commitStatus / checks need checks:write; cursorAccount permissions: "read" cannot post merge-box checks. Use "pr-write" or "contents-write".'
138
141
  );
139
142
  }
140
143
  return "contents-write";
@@ -43,6 +43,7 @@
43
43
  export * from "./api.js";
44
44
  export * from "./auth.js";
45
45
  export * from "./binding.js";
46
+ export * from "./checks.js";
46
47
  export * from "./defaults.js";
47
48
  export * from "./events.js";
48
49
  export * from "./forward.js";
@@ -546,6 +546,14 @@ export interface GitHubChannelOptions {
546
546
  * and sticky banner are opt-in via `commitStatus` / `banner`.
547
547
  */
548
548
  progress?: GitHubProgressConfig;
549
+ /**
550
+ * Declare that this channel posts its own CI checks
551
+ * (`ctx.github.createCheck` / `createGitHubCheck`). With `cursorAccount`,
552
+ * opts the minted token up to `permissions: "contents-write"` so the
553
+ * hosted lease includes `checks:write` — same opt-up as
554
+ * `progress.commitStatus`. Has no effect on App / PAT credentials.
555
+ */
556
+ checks?: boolean;
549
557
  /**
550
558
  * When false, skip posting assistant / error comments back to GitHub.
551
559
  * Defaults to true.
@@ -21,10 +21,11 @@ import type { SlackChannelPostsOptions, SlackMessage } from "./types.js";
21
21
 
22
22
  const CHANNEL_ID_PATTERN = /^[CG][A-Z0-9]+$/;
23
23
 
24
- /** Allowlist over channel ids and `#names` (names resolve lazily). */
24
+ /** Allowlist over channel ids and `#names` (names resolve lazily), or `*`. */
25
25
  export class SlackChannelAllowlist {
26
26
  private readonly ids = new Set<string>();
27
27
  private readonly names = new Set<string>();
28
+ private all = false;
28
29
 
29
30
  constructor(entries: readonly string[]) {
30
31
  for (const raw of entries) {
@@ -32,7 +33,9 @@ export class SlackChannelAllowlist {
32
33
  if (entry === "") {
33
34
  continue;
34
35
  }
35
- if (entry.startsWith("#")) {
36
+ if (entry === "*") {
37
+ this.all = true;
38
+ } else if (entry.startsWith("#")) {
36
39
  this.names.add(entry.slice(1).toLowerCase());
37
40
  } else if (CHANNEL_ID_PATTERN.test(entry)) {
38
41
  this.ids.add(entry);
@@ -44,7 +47,7 @@ export class SlackChannelAllowlist {
44
47
  }
45
48
 
46
49
  matches(channelId: string): boolean {
47
- return this.ids.has(channelId);
50
+ return this.all || this.ids.has(channelId);
48
51
  }
49
52
 
50
53
  unresolvedNames(): string[] {
@@ -142,6 +142,13 @@ export interface SlackInboundDispatchDeps extends SlackContinuationProbes {
142
142
  * dropped at parse time (loop safety).
143
143
  */
144
144
  botMentionGate?: SlackBotMentionGate;
145
+ /**
146
+ * When false, do not write `assistant.threads.setStatus` on a coalesced
147
+ * follow-up. Defaults to `true`. Channels that report progress their own
148
+ * way turn this off: the chip has no owner to clear it once the default
149
+ * `message.completed` handler is replaced, so it lingers past the turn.
150
+ */
151
+ followUpStatus?: boolean;
145
152
  /**
146
153
  * When false, skip the built-in `eval` directive. Defaults to true when
147
154
  * `host.evals` is present.
@@ -195,6 +202,8 @@ export async function dispatchInboundMessage(args: {
195
202
  log?: SlackLog;
196
203
  eventId?: string;
197
204
  evalCommandsEnabled?: boolean;
205
+ /** See {@link SlackInboundDispatchDeps.followUpStatus}. Defaults to `true`. */
206
+ followUpStatus?: boolean;
198
207
  /**
199
208
  * cursorAccount agent display name — stripped before parsing `eval …`
200
209
  * (`OncallBuddy eval list`).
@@ -477,8 +486,9 @@ export async function dispatchInboundMessage(args: {
477
486
  event_id: args.eventId,
478
487
  }
479
488
  );
480
- // Coalesce admits leave the active turn running; follow-ups drain into one
481
- // boundary turn when it finishes (no mid-turn tool/hook inject).
489
+ // Coalesce admits leave the active turn running: the follow-up is queued
490
+ // for the carrier to inject at the next tool boundary, and anything it
491
+ // does not reach in time drains into the boundary turn instead.
482
492
  if (session.coalesced === true) {
483
493
  log.info("session busy; coalescing follow-up", {
484
494
  kind,
@@ -487,8 +497,32 @@ export async function dispatchInboundMessage(args: {
487
497
  continuation,
488
498
  event_id: args.eventId,
489
499
  });
500
+ // Queue it for mid-turn injection too. The carrier drains this at the
501
+ // running turn's next tool boundary; whatever it does not reach in time
502
+ // still drains into the boundary turn, deduped by sourceTs.
503
+ try {
504
+ await args.host.slackNudges?.append({
505
+ continuationKey: continuation,
506
+ sourceTs: message.ts,
507
+ text: media.userText,
508
+ ...(message.author?.userName === undefined
509
+ ? {}
510
+ : { author: message.author.userName }),
511
+ });
512
+ } catch (nudgeError) {
513
+ log.warn("nudge append failed", {
514
+ error:
515
+ nudgeError instanceof Error
516
+ ? nudgeError.message
517
+ : String(nudgeError),
518
+ continuation,
519
+ event_id: args.eventId,
520
+ });
521
+ }
490
522
  try {
491
- await binding.thread.startTyping(FOLLOWUP_TYPING);
523
+ if (args.followUpStatus !== false) {
524
+ await binding.thread.startTyping(FOLLOWUP_TYPING);
525
+ }
492
526
  } catch (typingError) {
493
527
  log.error(
494
528
  `follow-up typing failed: ${
@@ -581,6 +615,7 @@ export function dispatchInboundFromEventBody(
581
615
  log,
582
616
  evalCommandsEnabled: deps.evalCommandsEnabled,
583
617
  cursorAgentName: deps.cursorAgentName,
618
+ followUpStatus: deps.followUpStatus,
584
619
  blockExternals: deps.blockExternals,
585
620
  ...(deps.hasContinuationSession === undefined
586
621
  ? {}
@@ -623,6 +658,7 @@ export function dispatchInboundFromEventBody(
623
658
  log,
624
659
  evalCommandsEnabled: deps.evalCommandsEnabled,
625
660
  cursorAgentName: deps.cursorAgentName,
661
+ followUpStatus: deps.followUpStatus,
626
662
  blockExternals: deps.blockExternals,
627
663
  ...(deps.hasContinuationSession === undefined
628
664
  ? {}
@@ -659,6 +695,7 @@ export function dispatchInboundFromEventBody(
659
695
  log,
660
696
  evalCommandsEnabled: false,
661
697
  cursorAgentName: deps.cursorAgentName,
698
+ followUpStatus: deps.followUpStatus,
662
699
  blockExternals: deps.blockExternals,
663
700
  ...(deps.hasContinuationSession === undefined
664
701
  ? {}
@@ -29,8 +29,8 @@ export interface SlackAppManifest {
29
29
  display_name: string;
30
30
  always_online: boolean;
31
31
  };
32
- assistant_view?: {
33
- assistant_description: string;
32
+ agent_view?: {
33
+ agent_description: string;
34
34
  suggested_prompts: Array<{ title: string; message: string }>;
35
35
  };
36
36
  app_home?: {
@@ -69,6 +69,7 @@ const BOT_SCOPES = [
69
69
  "channels:history",
70
70
  "chat:write",
71
71
  "files:read",
72
+ "files:write",
72
73
  "groups:history",
73
74
  "im:history",
74
75
  "im:read",
@@ -134,9 +135,9 @@ export function buildSlackManifest(
134
135
  : options.name;
135
136
  const botName = options.botDisplayName ?? toBotDisplayName(options.name, env);
136
137
  const description = options.description ?? defaultDescription(options.name);
137
- const includeAssistant = options.assistant !== false;
138
+ const includeAgentView = options.assistant !== false;
138
139
 
139
- const botScopes = includeAssistant
140
+ const botScopes = includeAgentView
140
141
  ? [...BOT_SCOPES]
141
142
  : BOT_SCOPES.filter((scope) => scope !== "assistant:write");
142
143
 
@@ -163,14 +164,12 @@ export function buildSlackManifest(
163
164
  messages_tab_enabled: true,
164
165
  messages_tab_read_only_enabled: false,
165
166
  },
166
- ...(includeAssistant
167
+ agent_view: includeAgentView
167
168
  ? {
168
- assistant_view: {
169
- assistant_description: description,
170
- suggested_prompts: suggested,
171
- },
169
+ agent_description: description,
170
+ suggested_prompts: suggested,
172
171
  }
173
- : {}),
172
+ : undefined,
174
173
  },
175
174
  oauth_config: {
176
175
  scopes: {
@@ -202,6 +202,24 @@ export class SlackNudgeStore {
202
202
  return (await this.listPending(continuationKey)).length > 0;
203
203
  }
204
204
 
205
+ /**
206
+ * How many follow-ups have reached the agent on this thread. Monotonic
207
+ * within a continuation, so a caller can spot a delivery by comparing two
208
+ * readings — unlike {@link hasUndelivered}, which races the carrier: both
209
+ * run at the same tool boundary, and a queued nudge drained before the
210
+ * first reading never looks pending at all.
211
+ */
212
+ async deliveredCount(continuationKey: string): Promise<number> {
213
+ if (continuationKey.trim() === "") {
214
+ return 0;
215
+ }
216
+ try {
217
+ return (await readdir(this.deliveredDir(continuationKey))).length;
218
+ } catch {
219
+ return 0;
220
+ }
221
+ }
222
+
205
223
  /**
206
224
  * Atomically move all pending nudges to delivered and return them.
207
225
  * Already-delivered source ts values are dropped (exactly-once).
@@ -238,6 +256,44 @@ export class SlackNudgeStore {
238
256
  return delivered;
239
257
  }
240
258
 
259
+ /**
260
+ * Settle a follow-up the caller is handling itself, so the carrier cannot
261
+ * inject it again. Used by the boundary drain: a follow-up the carrier did
262
+ * not reach becomes the next turn's prompt, and without this its pending
263
+ * record survives into that turn and gets injected on top of the ask it
264
+ * already is.
265
+ */
266
+ async markDelivered(
267
+ continuationKey: string,
268
+ sourceTs: string
269
+ ): Promise<void> {
270
+ const key = continuationKey.trim();
271
+ const ts = sourceTs.trim();
272
+ if (key === "" || ts === "") {
273
+ return;
274
+ }
275
+ const pending = await this.listPending(key);
276
+ const record = pending.find((item) => item.sourceTs === ts);
277
+ await mkdir(this.deliveredDir(key), { recursive: true });
278
+ const dest = this.deliveredPath(key, ts);
279
+ const tmp = `${dest}.${process.pid}.tmp`;
280
+ try {
281
+ await writeFile(
282
+ tmp,
283
+ `${JSON.stringify(record ?? { continuationKey: key, sourceTs: ts })}\n`,
284
+ "utf8"
285
+ );
286
+ await rename(tmp, dest);
287
+ } catch {
288
+ await rm(tmp, { force: true }).catch(() => {});
289
+ }
290
+ if (record !== undefined) {
291
+ await rm(join(this.pendingDir(key), `${record.id}.json`), {
292
+ force: true,
293
+ }).catch(() => {});
294
+ }
295
+ }
296
+
241
297
  async clear(continuationKey: string): Promise<void> {
242
298
  await rm(this.continuationDir(continuationKey), {
243
299
  recursive: true,
@@ -219,6 +219,7 @@ export function slackChannel<const Name extends string = string>(
219
219
  const mentionsEnabled = engagement.mentions !== false;
220
220
  const directMessagesEnabled = engagement.directMessages !== false;
221
221
  const evalCommandsEnabled = options.evalCommands !== false;
222
+ const followUpStatus = options.followUpStatus !== false;
222
223
  const channelPosts = engagement.channelPosts;
223
224
  if (channelPosts !== undefined) {
224
225
  const entries = channelPosts.allow.filter((entry) => entry.trim() !== "");
@@ -296,6 +297,7 @@ export function slackChannel<const Name extends string = string>(
296
297
  mentionsEnabled,
297
298
  directMessagesEnabled,
298
299
  evalCommandsEnabled,
300
+ followUpStatus,
299
301
  // cursorAccount: backend router already refuses externals.
300
302
  blockExternals: cursorAccount ? false : blockExternals,
301
303
  ...(args.hasContinuationSession === undefined
@@ -484,6 +486,7 @@ export function slackChannel<const Name extends string = string>(
484
486
  artifacts: args.artifacts,
485
487
  log: getLog(),
486
488
  evalCommandsEnabled,
489
+ followUpStatus,
487
490
  blockExternals,
488
491
  ...(args.hasContinuationSession === undefined
489
492
  ? {}
@@ -330,11 +330,18 @@ export type SlackBlockActionHandler = (
330
330
  */
331
331
  export interface SlackChannelPostsOptions {
332
332
  /**
333
- * Channels to watch: ids (`C…` / `G…`) or `#names`. Must be non-empty
334
- * (empty is a definition error). `#names` are resolved to ids via
335
- * `conversations.list` when the channel starts; the bot must be a member
336
- * of each watched channel to receive its messages. Socket Mode only —
337
- * `cursorAccount: true` rejects `engagement.channelPosts`.
333
+ * Channels to watch: ids (`C…` / `G…`), `#names`, or `"*"` for every
334
+ * channel the bot is in. Must be non-empty (empty is a definition error).
335
+ * `#names` are resolved to ids via `conversations.list` when the channel
336
+ * starts; the bot must be a member of each watched channel to receive its
337
+ * messages. Socket Mode only — `cursorAccount: true` rejects
338
+ * `engagement.channelPosts`.
339
+ *
340
+ * `"*"` suits agents whose handler decides per message (a thread the agent
341
+ * is already in, say) rather than per channel. Every delivered message then
342
+ * reaches {@link SlackChannelOptions.onChannelPost}, and each one costs a
343
+ * `users.info` external-sender check first unless
344
+ * {@link SlackAccessPolicy.blockExternals} is off.
338
345
  */
339
346
  allow: string[];
340
347
  /**
@@ -571,6 +578,16 @@ export interface SlackChannelOptions {
571
578
  * Defaults to `true`.
572
579
  */
573
580
  thinkingSteps?: boolean;
581
+ /**
582
+ * Write Slack's assistant status chip ("Saw your follow-up…") when a
583
+ * follow-up coalesces into a running turn. Defaults to `true`.
584
+ *
585
+ * Set to `false` on channels that report progress themselves: the chip is
586
+ * cleared by the default `message.completed` handler, so a channel that
587
+ * replaces that handler has nothing to take it down and it outlives the
588
+ * turn.
589
+ */
590
+ followUpStatus?: boolean;
574
591
  /**
575
592
  * Opt in to Slack delivery for human-in-the-loop tool approvals.
576
593
  *
@@ -634,7 +651,7 @@ export interface SlackManifestOptions {
634
651
  env: "dev" | "prod";
635
652
  /** Optional short description shown in the Slack app. */
636
653
  description?: string;
637
- /** Include `assistant_view` + `assistant:write` for typing status. */
654
+ /** Include `agent_view` + `assistant:write` for typing status. */
638
655
  assistant?: boolean;
639
656
  /**
640
657
  * Enable Slack interactivity (needed for Approve/Deny cards).
@@ -646,7 +663,7 @@ export interface SlackManifestOptions {
646
663
  * channel watch (`engagement.channelPosts`). Defaults to `false`.
647
664
  */
648
665
  channelPosts?: boolean;
649
- /** Seed suggested prompts into the manifest `assistant_view`. */
666
+ /** Seed suggested prompts into the manifest `agent_view`. */
650
667
  suggestedPrompts?: Array<{ title: string; message: string }>;
651
668
  /**
652
669
  * Emit `settings.managed_app_settings`. Defaults to `true` — Cursor
@@ -4,7 +4,8 @@
4
4
  * A connection gives tools from an external MCP server. The filename
5
5
  * becomes the connection (server) name. Available to the agent (SDK
6
6
  * `mcpServers`), host (`ctx.host.mcp`), and channel/schedule handlers.
7
- * Pass `hostOnly: true` to keep it off the model (still on `host.mcp`).
7
+ * Pass `advertiseTools: true` for named tools on local turns. Pass
8
+ * `hostOnly: true` to keep it off the model (still on `host.mcp`).
8
9
  *
9
10
  * ```ts
10
11
  * import { defineConnection } from "@anysphere/agent-serve/connections";
@@ -42,13 +43,24 @@
42
43
  * to be signed in (`agent-serve login` or `CURSOR_API_KEY`):
43
44
  *
44
45
  * ```ts
45
- * // agent/mcp-connections/cursor.ts — every connected server (no allowlist)
46
- * export default defineConnection({ cursorAccount: true });
46
+ * // agent/mcp-connections/account.ts — every connected server (no allowlist)
47
+ * export default defineConnection({
48
+ * cursorAccount: true,
49
+ * advertiseTools: true,
50
+ * });
47
51
  * // equivalent explicit form:
48
- * export default defineConnection({ cursorAccount: true, servers: "*" });
52
+ * export default defineConnection({
53
+ * cursorAccount: true,
54
+ * servers: "*",
55
+ * advertiseTools: true,
56
+ * });
49
57
  *
50
58
  * // or restrict to specific connectors by name
51
- * export default defineConnection({ cursorAccount: true, servers: ["Linear"] });
59
+ * export default defineConnection({
60
+ * cursorAccount: true,
61
+ * servers: ["Linear"],
62
+ * advertiseTools: true,
63
+ * });
52
64
  * ```
53
65
  *
54
66
  * A **peer MCP connection** addresses another agent mounted on the same serve
package/src/index.ts CHANGED
@@ -132,6 +132,8 @@ export type {
132
132
  export { defineSchedule } from "./schedules.js";
133
133
  export { defineSkill } from "./skills.js";
134
134
  export type {
135
+ DurableSessionRecord,
136
+ DurableSessionSummary,
135
137
  StorageABTable,
136
138
  StorageCasTable,
137
139
  StorageConfig,
@@ -144,8 +146,11 @@ export type {
144
146
  } from "./storage.js";
145
147
  export {
146
148
  defineStorage,
149
+ isDurableSessionRecordKey,
150
+ parseDurableSessionRecord,
147
151
  STORAGE_KEY_ROOT,
148
152
  storageKeys,
153
+ summarizeDurableSessionRecord,
149
154
  } from "./storage.js";
150
155
  export { defineTool } from "./tools.js";
151
156
  export type * from "./types.js";