@cursor/july 0.1.99 → 0.1.100

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 (267) hide show
  1. package/AGENTS.md +4 -2
  2. package/dist/channels/github/defaults.d.ts.map +1 -1
  3. package/dist/channels/github/defaults.js +28 -3
  4. package/dist/channels/github/progress.d.ts +7 -0
  5. package/dist/channels/github/progress.d.ts.map +1 -1
  6. package/dist/channels/slack/api.d.ts +16 -7
  7. package/dist/channels/slack/api.d.ts.map +1 -1
  8. package/dist/channels/slack/api.js +29 -13
  9. package/dist/channels/slack/constants.d.ts +6 -0
  10. package/dist/channels/slack/constants.d.ts.map +1 -1
  11. package/dist/channels/slack/constants.js +6 -0
  12. package/dist/channels/slack/defaults.d.ts +9 -4
  13. package/dist/channels/slack/defaults.d.ts.map +1 -1
  14. package/dist/channels/slack/defaults.js +154 -76
  15. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  16. package/dist/channels/slack/dispatch.js +3 -3
  17. package/dist/channels/slack/inbound.d.ts +9 -0
  18. package/dist/channels/slack/inbound.d.ts.map +1 -1
  19. package/dist/channels/slack/inbound.js +14 -0
  20. package/dist/channels/slack/index.d.ts +1 -0
  21. package/dist/channels/slack/index.d.ts.map +1 -1
  22. package/dist/channels/slack/index.js +1 -0
  23. package/dist/channels/slack/live-delivery.d.ts +5 -0
  24. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  25. package/dist/channels/slack/live-delivery.js +107 -19
  26. package/dist/channels/slack/reasoning-card.d.ts +38 -0
  27. package/dist/channels/slack/reasoning-card.d.ts.map +1 -0
  28. package/dist/channels/slack/reasoning-card.js +104 -0
  29. package/dist/channels/slack/redact.d.ts +2 -0
  30. package/dist/channels/slack/redact.d.ts.map +1 -0
  31. package/dist/channels/slack/redact.js +5 -0
  32. package/dist/channels/slack/reply-options.d.ts +40 -0
  33. package/dist/channels/slack/reply-options.d.ts.map +1 -0
  34. package/dist/channels/slack/reply-options.js +150 -0
  35. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  36. package/dist/channels/slack/slack-channel.js +8 -4
  37. package/dist/channels/slack/stream.d.ts +22 -10
  38. package/dist/channels/slack/stream.d.ts.map +1 -1
  39. package/dist/channels/slack/stream.js +9 -16
  40. package/dist/channels/slack/tool-cards.d.ts +22 -0
  41. package/dist/channels/slack/tool-cards.d.ts.map +1 -0
  42. package/dist/channels/slack/tool-cards.js +178 -0
  43. package/dist/channels/slack/types.d.ts +145 -6
  44. package/dist/channels/slack/types.d.ts.map +1 -1
  45. package/dist/docs/404.html +2 -2
  46. package/dist/docs/ab.html +6 -6
  47. package/dist/docs/ab.md +1 -1
  48. package/dist/docs/assets/{ab.md.DJo5r4R-.js → ab.md.mlVgqvSk.js} +1 -1
  49. package/dist/docs/assets/{app.BplEAZ3S.js → app.DZ1e0Ycq.js} +1 -1
  50. package/dist/docs/assets/{building-with-agents.md.DI4mEzlt.js → building-with-agents.md.CUSWxlP_.js} +2 -2
  51. package/dist/docs/assets/chunks/@localSearchIndexroot.j70vvPL4.js +1 -0
  52. package/dist/docs/assets/chunks/{VPLocalSearchBox.CHXyv4wP.js → VPLocalSearchBox.l8omwc6D.js} +1 -1
  53. package/dist/docs/assets/chunks/{theme.BTcsZmjQ.js → theme.JVcD6gel.js} +2 -2
  54. package/dist/docs/assets/{evals.md.lfJoEVc8.js → evals.md.CPzDAwoH.js} +1 -1
  55. package/dist/docs/assets/{guides_github.md.Cnh2mL4a.js → guides_github.md.BtPr9GaP.js} +1 -1
  56. package/dist/docs/assets/{guides_mcp-oauth.md.CN-6YmTJ.js → guides_mcp-oauth.md.Dp6cDP7f.js} +1 -1
  57. package/dist/docs/assets/{guides_opentelemetry.md.bmPmkvJu.js → guides_opentelemetry.md.BVTXDCRg.js} +2 -2
  58. package/dist/docs/assets/{guides_slack.md.VDXQV3ja.js → guides_slack.md.9oHPye9o.js} +19 -3
  59. package/dist/docs/assets/{guides_slack.md.VDXQV3ja.lean.js → guides_slack.md.9oHPye9o.lean.js} +1 -1
  60. package/dist/docs/assets/hillclimbing.md.CpTGTCle.js +4 -0
  61. package/dist/docs/assets/{index.md.CVeRUOeZ.js → index.md.Bb4k8kUm.js} +1 -1
  62. package/dist/docs/assets/{quickstart.md.Nj_LjW_a.js → quickstart.md.DdQOF7Y8.js} +1 -1
  63. package/dist/docs/assets/{reference_connections.md.DxldvyIB.js → reference_connections.md.BiGoBAk2.js} +1 -1
  64. package/dist/docs/assets/scaffolding-agents.md.em43xlY1.js +1 -0
  65. package/dist/docs/assets/skills_ab.md.CsFNatVx.js +26 -0
  66. package/dist/docs/assets/skills_ab.md.CsFNatVx.lean.js +1 -0
  67. package/dist/docs/assets/skills_create-agent.md.BVoWPcan.js +8 -0
  68. package/dist/docs/assets/skills_create-agent.md.BVoWPcan.lean.js +1 -0
  69. package/dist/docs/assets/skills_debug.md.CDbPhHfg.js +1 -0
  70. package/dist/docs/assets/skills_debug.md.CDbPhHfg.lean.js +1 -0
  71. package/dist/docs/assets/skills_evals.md.723kpUmA.js +25 -0
  72. package/dist/docs/assets/skills_evals.md.723kpUmA.lean.js +1 -0
  73. package/dist/docs/assets/skills_framework-map.md.BTi817yv.js +1 -0
  74. package/dist/docs/assets/skills_framework-map.md.BTi817yv.lean.js +1 -0
  75. package/dist/docs/assets/skills_github.md.D0JahM8c.js +16 -0
  76. package/dist/docs/assets/skills_github.md.D0JahM8c.lean.js +1 -0
  77. package/dist/docs/assets/skills_hillclimb.md.B_zJerxA.js +7 -0
  78. package/dist/docs/assets/skills_hillclimb.md.B_zJerxA.lean.js +1 -0
  79. package/dist/docs/assets/skills_index.md.DKwIxzGg.js +1 -0
  80. package/dist/docs/assets/skills_index.md.DKwIxzGg.lean.js +1 -0
  81. package/dist/docs/assets/skills_mcp-auth.md.DGvFP3HE.js +18 -0
  82. package/dist/docs/assets/skills_mcp-auth.md.DGvFP3HE.lean.js +1 -0
  83. package/dist/docs/assets/skills_otel.md.CgiZryR3.js +8 -0
  84. package/dist/docs/assets/skills_otel.md.CgiZryR3.lean.js +1 -0
  85. package/dist/docs/assets/skills_setup-slack.md.BBgx8lUz.js +20 -0
  86. package/dist/docs/assets/skills_setup-slack.md.BBgx8lUz.lean.js +1 -0
  87. package/dist/docs/assets/{troubleshooting.md.DCiPBhYs.js → troubleshooting.md.Cus_YZga.js} +1 -1
  88. package/dist/docs/building-with-agents.html +6 -6
  89. package/dist/docs/building-with-agents.md +14 -11
  90. package/dist/docs/concepts.html +4 -4
  91. package/dist/docs/deployment.html +4 -4
  92. package/dist/docs/evals.html +6 -6
  93. package/dist/docs/evals.md +1 -1
  94. package/dist/docs/guides/agent-to-agent.html +4 -4
  95. package/dist/docs/guides/cloud-runtime.html +4 -4
  96. package/dist/docs/guides/convert-automation.html +4 -4
  97. package/dist/docs/guides/github.html +5 -5
  98. package/dist/docs/guides/github.md +1 -1
  99. package/dist/docs/guides/human-in-the-loop.html +4 -4
  100. package/dist/docs/guides/mcp-oauth.html +5 -5
  101. package/dist/docs/guides/mcp-oauth.md +1 -1
  102. package/dist/docs/guides/opentelemetry.html +7 -7
  103. package/dist/docs/guides/opentelemetry.md +2 -2
  104. package/dist/docs/guides/slack.html +22 -6
  105. package/dist/docs/guides/slack.md +80 -1
  106. package/dist/docs/guides/webhooks.html +5 -5
  107. package/dist/docs/hashmap.json +1 -1
  108. package/dist/docs/hillclimbing.html +6 -6
  109. package/dist/docs/hillclimbing.md +5 -5
  110. package/dist/docs/index.html +6 -6
  111. package/dist/docs/index.md +1 -1
  112. package/dist/docs/llms-full.txt +1041 -30
  113. package/dist/docs/llms.txt +14 -0
  114. package/dist/docs/quickstart.html +5 -5
  115. package/dist/docs/quickstart.md +1 -1
  116. package/dist/docs/reference/agent-config.html +4 -4
  117. package/dist/docs/reference/artifacts.html +4 -4
  118. package/dist/docs/reference/channels.html +4 -4
  119. package/dist/docs/reference/cli.html +4 -4
  120. package/dist/docs/reference/connections.html +6 -6
  121. package/dist/docs/reference/connections.md +1 -1
  122. package/dist/docs/reference/hooks.html +4 -4
  123. package/dist/docs/reference/http-api.html +4 -4
  124. package/dist/docs/reference/instructions.html +4 -4
  125. package/dist/docs/reference/playground.html +4 -4
  126. package/dist/docs/reference/project-layout.html +4 -4
  127. package/dist/docs/reference/prompt.html +4 -4
  128. package/dist/docs/reference/schedules.html +4 -4
  129. package/dist/docs/reference/sessions.html +4 -4
  130. package/dist/docs/reference/skills.html +4 -4
  131. package/dist/docs/reference/subagents.html +4 -4
  132. package/dist/docs/reference/tools.html +4 -4
  133. package/dist/docs/scaffolding-agents.html +5 -5
  134. package/dist/docs/scaffolding-agents.md +3 -3
  135. package/dist/docs/skills/ab.html +52 -0
  136. package/dist/docs/skills/ab.md +50 -0
  137. package/dist/docs/skills/create-agent.html +34 -0
  138. package/dist/docs/skills/create-agent.md +160 -0
  139. package/dist/docs/skills/debug.html +27 -0
  140. package/dist/docs/skills/debug.md +36 -0
  141. package/dist/docs/skills/evals.html +51 -0
  142. package/dist/docs/skills/evals.md +99 -0
  143. package/dist/docs/skills/framework-map.html +27 -0
  144. package/dist/docs/skills/framework-map.md +95 -0
  145. package/dist/docs/skills/github.html +42 -0
  146. package/dist/docs/skills/github.md +93 -0
  147. package/dist/docs/skills/hillclimb.html +33 -0
  148. package/dist/docs/skills/hillclimb.md +55 -0
  149. package/dist/docs/skills/index.html +27 -0
  150. package/dist/docs/skills/index.md +21 -0
  151. package/dist/docs/skills/mcp-auth.html +44 -0
  152. package/dist/docs/skills/mcp-auth.md +76 -0
  153. package/dist/docs/skills/otel.html +34 -0
  154. package/dist/docs/skills/otel.md +48 -0
  155. package/dist/docs/skills/setup-slack.html +46 -0
  156. package/dist/docs/skills/setup-slack.md +141 -0
  157. package/dist/docs/storage.html +4 -4
  158. package/dist/docs/templates/agentic-owners.html +4 -4
  159. package/dist/docs/templates/agents-md.html +4 -4
  160. package/dist/docs/templates/code-wiki.html +4 -4
  161. package/dist/docs/templates/demo.html +4 -4
  162. package/dist/docs/templates/pr-autofixer.html +4 -4
  163. package/dist/docs/templates/security-help.html +4 -4
  164. package/dist/docs/templates/security-reviewer.html +4 -4
  165. package/dist/docs/templates/triage.html +4 -4
  166. package/dist/docs/troubleshooting.html +5 -5
  167. package/dist/docs/troubleshooting.md +1 -1
  168. package/dist/files-backends/cursor-hosted.d.ts +6 -0
  169. package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
  170. package/dist/files-backends/cursor-hosted.js +6 -9
  171. package/dist/internal/cli-deploy.js +2 -2
  172. package/dist/internal/conversation-mirror.d.ts.map +1 -1
  173. package/dist/internal/conversation-mirror.js +3 -4
  174. package/dist/internal/cursor/hosted-store-secrets.d.ts +15 -0
  175. package/dist/internal/cursor/hosted-store-secrets.d.ts.map +1 -0
  176. package/dist/internal/cursor/hosted-store-secrets.js +48 -0
  177. package/dist/internal/deploy-client.d.ts +6 -6
  178. package/dist/internal/deploy-client.js +7 -7
  179. package/dist/internal/discovery.d.ts.map +1 -1
  180. package/dist/internal/discovery.js +11 -1
  181. package/dist/internal/framework-file-storage.d.ts +12 -5
  182. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  183. package/dist/internal/framework-file-storage.js +36 -5
  184. package/dist/internal/framework-storage-selection.d.ts +12 -12
  185. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  186. package/dist/internal/framework-storage-selection.js +33 -39
  187. package/dist/internal/session-run-log.d.ts +3 -3
  188. package/dist/internal/session-run-log.d.ts.map +1 -1
  189. package/dist/internal/session-run-log.js +6 -7
  190. package/dist/internal/storage-coordinator.d.ts +0 -6
  191. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  192. package/dist/internal/storage-coordinator.js +1 -9
  193. package/dist/playground/assets/index-BszoQDc6.css +1 -0
  194. package/dist/playground/assets/{index-jzNbJENa.js → index-DVs98vPL.js} +1 -1
  195. package/dist/playground/index.html +2 -2
  196. package/dist/storage-backends/cursor-hosted-v2.d.ts +83 -0
  197. package/dist/storage-backends/cursor-hosted-v2.d.ts.map +1 -0
  198. package/dist/storage-backends/cursor-hosted-v2.js +164 -0
  199. package/dist/storage-backends/cursor-hosted.d.ts +6 -0
  200. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  201. package/dist/storage-backends/cursor-hosted.js +6 -1
  202. package/dist/types.d.ts +5 -0
  203. package/dist/types.d.ts.map +1 -1
  204. package/docs/README.md +1 -1
  205. package/docs/ab.md +1 -1
  206. package/docs/building-with-agents.md +14 -11
  207. package/docs/evals.md +1 -1
  208. package/docs/guides/github.md +1 -1
  209. package/docs/guides/mcp-oauth.md +1 -1
  210. package/docs/guides/opentelemetry.md +2 -2
  211. package/docs/guides/slack.md +80 -1
  212. package/docs/hillclimbing.md +5 -5
  213. package/docs/quickstart.md +1 -1
  214. package/docs/reference/connections.md +1 -1
  215. package/docs/scaffolding-agents.md +3 -3
  216. package/docs/skills/index.md +26 -0
  217. package/docs/troubleshooting.md +1 -1
  218. package/package.json +1 -1
  219. package/src/channels/github/defaults.ts +36 -3
  220. package/src/channels/github/progress.ts +7 -0
  221. package/src/channels/slack/api.ts +35 -17
  222. package/src/channels/slack/constants.ts +6 -0
  223. package/src/channels/slack/defaults.ts +173 -92
  224. package/src/channels/slack/dispatch.ts +8 -1
  225. package/src/channels/slack/inbound.ts +17 -0
  226. package/src/channels/slack/index.ts +1 -0
  227. package/src/channels/slack/live-delivery.ts +131 -20
  228. package/src/channels/slack/reasoning-card.ts +140 -0
  229. package/src/channels/slack/redact.ts +6 -0
  230. package/src/channels/slack/reply-options.ts +229 -0
  231. package/src/channels/slack/slack-channel.ts +7 -2
  232. package/src/channels/slack/stream.ts +31 -24
  233. package/src/channels/slack/tool-cards.ts +221 -0
  234. package/src/channels/slack/types.ts +159 -6
  235. package/src/files-backends/cursor-hosted.ts +15 -5
  236. package/src/internal/cli-deploy.ts +2 -2
  237. package/src/internal/conversation-mirror.ts +3 -8
  238. package/src/internal/cursor/hosted-store-secrets.ts +72 -0
  239. package/src/internal/deploy-client.ts +10 -10
  240. package/src/internal/discovery.ts +9 -1
  241. package/src/internal/framework-file-storage.ts +41 -5
  242. package/src/internal/framework-storage-selection.ts +31 -44
  243. package/src/internal/session-run-log.ts +6 -11
  244. package/src/internal/storage-coordinator.ts +1 -11
  245. package/src/storage-backends/cursor-hosted-v2.ts +219 -0
  246. package/src/storage-backends/cursor-hosted.ts +6 -1
  247. package/src/types.ts +5 -0
  248. package/dist/docs/assets/chunks/@localSearchIndexroot.DpuI4i7Z.js +0 -1
  249. package/dist/docs/assets/hillclimbing.md.DhESf3OO.js +0 -4
  250. package/dist/docs/assets/scaffolding-agents.md.D7UUkWw0.js +0 -1
  251. package/dist/internal/null-kv-storage.d.ts +0 -48
  252. package/dist/internal/null-kv-storage.d.ts.map +0 -1
  253. package/dist/internal/null-kv-storage.js +0 -58
  254. package/dist/playground/assets/index-D7rGZNjy.css +0 -1
  255. package/src/internal/null-kv-storage.ts +0 -74
  256. /package/dist/docs/assets/{ab.md.DJo5r4R-.lean.js → ab.md.mlVgqvSk.lean.js} +0 -0
  257. /package/dist/docs/assets/{building-with-agents.md.DI4mEzlt.lean.js → building-with-agents.md.CUSWxlP_.lean.js} +0 -0
  258. /package/dist/docs/assets/{evals.md.lfJoEVc8.lean.js → evals.md.CPzDAwoH.lean.js} +0 -0
  259. /package/dist/docs/assets/{guides_github.md.Cnh2mL4a.lean.js → guides_github.md.BtPr9GaP.lean.js} +0 -0
  260. /package/dist/docs/assets/{guides_mcp-oauth.md.CN-6YmTJ.lean.js → guides_mcp-oauth.md.Dp6cDP7f.lean.js} +0 -0
  261. /package/dist/docs/assets/{guides_opentelemetry.md.bmPmkvJu.lean.js → guides_opentelemetry.md.BVTXDCRg.lean.js} +0 -0
  262. /package/dist/docs/assets/{hillclimbing.md.DhESf3OO.lean.js → hillclimbing.md.CpTGTCle.lean.js} +0 -0
  263. /package/dist/docs/assets/{index.md.CVeRUOeZ.lean.js → index.md.Bb4k8kUm.lean.js} +0 -0
  264. /package/dist/docs/assets/{quickstart.md.Nj_LjW_a.lean.js → quickstart.md.DdQOF7Y8.lean.js} +0 -0
  265. /package/dist/docs/assets/{reference_connections.md.DxldvyIB.lean.js → reference_connections.md.BiGoBAk2.lean.js} +0 -0
  266. /package/dist/docs/assets/{scaffolding-agents.md.D7UUkWw0.lean.js → scaffolding-agents.md.em43xlY1.lean.js} +0 -0
  267. /package/dist/docs/assets/{troubleshooting.md.DCiPBhYs.lean.js → troubleshooting.md.Cus_YZga.lean.js} +0 -0
@@ -4,11 +4,36 @@
4
4
 
5
5
  export type StreamTaskStatus = "pending" | "in_progress" | "complete" | "error";
6
6
 
7
+ /**
8
+ * One update to a task card. Slack keeps one card per `id`: `title` and
9
+ * `status` replace the previous values, `details` and `output` are appended
10
+ * to what the card already shows.
11
+ */
7
12
  export interface StreamTaskChunk {
8
13
  type: "task_update";
9
14
  id: string;
10
15
  title: string;
11
16
  status: StreamTaskStatus;
17
+ details?: string;
18
+ output?: string;
19
+ }
20
+
21
+ export interface StreamMarkdownChunk {
22
+ type: "markdown_text";
23
+ text: string;
24
+ }
25
+
26
+ /**
27
+ * A `chat.*Stream` chunk. Slack fixes a stream's mode at `chat.startStream`:
28
+ * one opened with bare `markdown_text` rejects every later `chunks` append
29
+ * (`streaming_mode_mismatch`), and vice versa. The live-delivery strategy
30
+ * therefore always speaks chunks, wrapping text as `markdown_text` chunks so
31
+ * tool-step `task_update`s can share the same message.
32
+ */
33
+ export type StreamChunk = StreamMarkdownChunk | StreamTaskChunk;
34
+
35
+ export function markdownChunk(text: string): StreamMarkdownChunk {
36
+ return { type: "markdown_text", text };
12
37
  }
13
38
 
14
39
  /**
@@ -36,28 +61,10 @@ export function computeStreamAppend(
36
61
  return "";
37
62
  }
38
63
 
39
- /** Build task_update chunks for tool calls. */
40
- export function buildTaskChunksForCalls(
41
- calls: Array<{ callId: string; toolName: string }>,
42
- status: StreamTaskStatus
43
- ): StreamTaskChunk[] {
44
- return calls.map((call) => ({
45
- type: "task_update" as const,
46
- id: call.callId,
47
- title: call.toolName,
48
- status,
49
- }));
50
- }
51
-
52
- export function buildTaskChunkForResult(args: {
53
- callId: string;
54
- toolName: string;
55
- isError: boolean;
56
- }): StreamTaskChunk {
57
- return {
58
- type: "task_update",
59
- id: args.callId,
60
- title: args.toolName,
61
- status: args.isError ? "error" : "complete",
62
- };
64
+ /** Clamp to `max` characters, ending in an ellipsis when cut. */
65
+ export function truncateCardText(text: string, max: number): string {
66
+ if (text.length <= max) {
67
+ return text;
68
+ }
69
+ return `${text.slice(0, max - 1)}…`;
63
70
  }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Task cards for tool calls inside the streamed reply. One card per key:
3
+ * the call id for `per-call`, the tool name for `per-tool`. A card's title
4
+ * counts its calls and its status follows the whole group; with one call
5
+ * per key that reduces to the plain per-call card.
6
+ */
7
+
8
+ import { redactSecretTokens } from "./redact.js";
9
+ import type { ResolvedSlackToolCards } from "./reply-options.js";
10
+ import { type StreamTaskChunk, truncateCardText } from "./stream.js";
11
+ import type {
12
+ SlackChannelState,
13
+ SlackToolCall,
14
+ SlackToolResult,
15
+ ToolCardCount,
16
+ } from "./types.js";
17
+
18
+ /** The argument the playground puts in a built-in tool's header. */
19
+ const TOOL_PRIMARY_ARGS: Readonly<Record<string, readonly string[]>> = {
20
+ grep: ["pattern"],
21
+ glob: ["glob_pattern", "globPattern"],
22
+ read: ["path", "file_path", "target_file"],
23
+ shell: ["command", "cmd"],
24
+ task: ["description", "prompt"],
25
+ };
26
+ /** For other tools: the telling key, if any. */
27
+ const GENERIC_PRIMARY_ARGS = [
28
+ "pattern",
29
+ "query",
30
+ "command",
31
+ "cmd",
32
+ "path",
33
+ "file",
34
+ "file_path",
35
+ "target_file",
36
+ "url",
37
+ "prompt",
38
+ "description",
39
+ "name",
40
+ "message",
41
+ ];
42
+ const PATH_ARGS = new Set([
43
+ "path",
44
+ "file",
45
+ "file_path",
46
+ "target_file",
47
+ "target_directory",
48
+ "targetDirectory",
49
+ ]);
50
+ const LINE_MAX = 80;
51
+ const JSON_MAX = 120;
52
+
53
+ function oneLine(text: string, max: number): string {
54
+ return truncateCardText(
55
+ redactSecretTokens(text).replace(/\s+/g, " ").trim(),
56
+ max
57
+ );
58
+ }
59
+
60
+ /** Clamp the middle so both ends stay visible; tail-biased to keep filenames. */
61
+ function clampPath(text: string, max: number): string {
62
+ const one = redactSecretTokens(text).replace(/\s+/g, " ").trim();
63
+ if (one.length <= max) {
64
+ return one;
65
+ }
66
+ const budget = max - 1;
67
+ const head = Math.max(1, Math.floor(budget * 0.35));
68
+ const tail = Math.max(1, budget - head);
69
+ return `${one.slice(0, head)}…${one.slice(one.length - tail)}`;
70
+ }
71
+
72
+ /**
73
+ * Default tool-card line for a call, matching the playground's tool header:
74
+ * the grep pattern, the glob pattern, the file path, the shell command. Other
75
+ * tools show a lone string argument bare, else compact JSON. No key labels,
76
+ * no knobs like `headLimit`.
77
+ */
78
+ export function defaultToolCardDetails(
79
+ call: SlackToolCall
80
+ ): string | undefined {
81
+ const args = call.args;
82
+ if (args === undefined || args === null) {
83
+ return undefined;
84
+ }
85
+ if (typeof args === "string") {
86
+ const line = oneLine(args, LINE_MAX);
87
+ return line === "" ? undefined : line;
88
+ }
89
+ if (typeof args !== "object" || Array.isArray(args)) {
90
+ return oneLine(JSON.stringify(args), JSON_MAX);
91
+ }
92
+ const record = args as Record<string, unknown>;
93
+ for (const key of TOOL_PRIMARY_ARGS[call.toolName] ?? GENERIC_PRIMARY_ARGS) {
94
+ const value = record[key];
95
+ if (typeof value === "string" && value.trim() !== "") {
96
+ return PATH_ARGS.has(key)
97
+ ? clampPath(value, LINE_MAX)
98
+ : oneLine(value, LINE_MAX);
99
+ }
100
+ }
101
+ const entries = Object.entries(record);
102
+ if (entries.length === 0) {
103
+ return undefined;
104
+ }
105
+ const lone = entries.length === 1 ? entries[0]?.[1] : undefined;
106
+ if (typeof lone === "string" && lone.trim() !== "") {
107
+ return oneLine(lone, LINE_MAX);
108
+ }
109
+ return oneLine(JSON.stringify(record), JSON_MAX);
110
+ }
111
+
112
+ export interface ToolCards {
113
+ forCalls(
114
+ state: SlackChannelState,
115
+ calls: readonly SlackToolCall[]
116
+ ): StreamTaskChunk[];
117
+ forResult(state: SlackChannelState, result: SlackToolResult): StreamTaskChunk;
118
+ }
119
+
120
+ function tallyFor(state: SlackChannelState, key: string): ToolCardCount {
121
+ if (state.toolCardCounts === undefined) {
122
+ state.toolCardCounts = {};
123
+ }
124
+ const existing = state.toolCardCounts[key];
125
+ if (existing !== undefined) {
126
+ return existing;
127
+ }
128
+ const created: ToolCardCount = { started: 0, done: 0, errors: 0 };
129
+ state.toolCardCounts[key] = created;
130
+ return created;
131
+ }
132
+
133
+ /**
134
+ * Slack appends each update's text to the card verbatim, so a group's later
135
+ * batch of lines brings its own line break.
136
+ */
137
+ function appendLines(
138
+ tally: ToolCardCount,
139
+ field: "details" | "output",
140
+ lines: readonly string[]
141
+ ): string | undefined {
142
+ if (lines.length === 0) {
143
+ return undefined;
144
+ }
145
+ const already = tally[field] ?? 0;
146
+ tally[field] = already + lines.length;
147
+ return `${already > 0 ? "\n" : ""}${lines.join("\n")}`;
148
+ }
149
+
150
+ function present(line: string | undefined): line is string {
151
+ return line !== undefined && line !== "";
152
+ }
153
+
154
+ export function createToolCards(
155
+ cards: ResolvedSlackToolCards,
156
+ toolLabel: (toolName: string) => string
157
+ ): ToolCards {
158
+ const keyOf = (call: { callId: string; toolName: string }): string =>
159
+ cards.group === "per-call" ? call.callId : `tool:${call.toolName}`;
160
+ const title = (toolName: string, started: number): string => {
161
+ const label = toolLabel(toolName);
162
+ return started > 1 ? `${label} ×${started}` : label;
163
+ };
164
+
165
+ return {
166
+ forCalls(state, calls) {
167
+ const groups = new Map<string, SlackToolCall[]>();
168
+ for (const call of calls) {
169
+ const key = keyOf(call);
170
+ groups.set(key, [...(groups.get(key) ?? []), call]);
171
+ }
172
+ return [...groups.entries()].map(([key, group]) => {
173
+ const tally = tallyFor(state, key);
174
+ tally.started += group.length;
175
+ const chunk: StreamTaskChunk = {
176
+ type: "task_update",
177
+ id: key,
178
+ title: title(group[0]!.toolName, tally.started),
179
+ status: "in_progress",
180
+ };
181
+ const details = appendLines(
182
+ tally,
183
+ "details",
184
+ group.map((call) => cards.details?.(call)).filter(present)
185
+ );
186
+ if (details !== undefined) {
187
+ chunk.details = details;
188
+ }
189
+ return chunk;
190
+ });
191
+ },
192
+
193
+ forResult(state, result) {
194
+ const tally = tallyFor(state, keyOf(result));
195
+ tally.done += 1;
196
+ if (result.isError) {
197
+ tally.errors += 1;
198
+ }
199
+ const chunk: StreamTaskChunk = {
200
+ type: "task_update",
201
+ id: keyOf(result),
202
+ title: title(result.toolName, tally.started),
203
+ status:
204
+ tally.done < tally.started
205
+ ? "in_progress"
206
+ : tally.errors > 0
207
+ ? "error"
208
+ : "complete",
209
+ };
210
+ const output = appendLines(
211
+ tally,
212
+ "output",
213
+ [cards.output?.(result)].filter(present)
214
+ );
215
+ if (output !== undefined) {
216
+ chunk.output = output;
217
+ }
218
+ return chunk;
219
+ },
220
+ };
221
+ }
@@ -79,6 +79,8 @@ export interface SlackChannelState {
79
79
  * for the rest of the turn.
80
80
  */
81
81
  streamingUnavailable?: boolean;
82
+ /** The open stream hit Slack's size cap; appends stop until it closes. */
83
+ streamFull?: boolean;
82
84
  /** Editable progress message ts when `chat.startStream` is unavailable. */
83
85
  progressTs: string | null;
84
86
  /**
@@ -86,8 +88,151 @@ export interface SlackChannelState {
86
88
  * replacing Approve/Deny buttons after a decision).
87
89
  */
88
90
  approvalMessageByCallId?: Record<string, string>;
91
+ /** `reply.reasoningCard` bookkeeping for the current turn. */
92
+ reasoningCard?: ReasoningCardState;
93
+ /** `reply.toolCards` per-tool tallies for the current turn, keyed by tool name. */
94
+ toolCardCounts?: Record<string, ToolCardCount>;
89
95
  }
90
96
 
97
+ /**
98
+ * Slack appends a task card's `details` across updates, so the reasoning
99
+ * card is fed deltas: `blockSent` is how much of the current reasoning block
100
+ * is already on Slack, `pending` is buffered text awaiting a flush.
101
+ */
102
+ export interface ReasoningCardState {
103
+ status: "in_progress" | "complete" | null;
104
+ blockSent: number | null;
105
+ pending: string;
106
+ lastFlushMs: number | null;
107
+ chars: number;
108
+ }
109
+
110
+ /**
111
+ * `details` / `output` count the lines already on the card, so a later
112
+ * batch can start on a new line (Slack appends updates verbatim).
113
+ */
114
+ export interface ToolCardCount {
115
+ started: number;
116
+ done: number;
117
+ errors: number;
118
+ details?: number;
119
+ output?: number;
120
+ }
121
+
122
+ /** A tool call as the model requested it (`actions.requested`). */
123
+ export interface SlackToolCall {
124
+ callId: string;
125
+ toolName: string;
126
+ args?: unknown;
127
+ }
128
+
129
+ /** A finished tool call (`action.result`). */
130
+ export interface SlackToolResult {
131
+ callId: string;
132
+ toolName: string;
133
+ output?: unknown;
134
+ isError: boolean;
135
+ }
136
+
137
+ /**
138
+ * What the status chip under the thread shows while a turn runs. Slack caps
139
+ * each line at 50 characters. While a `chat.startStream` is open Slack shows
140
+ * its own generic "is working…" instead, so in `"stream"` mode these lines
141
+ * are only visible until the first chunk goes out.
142
+ */
143
+ export interface SlackStatusOptions {
144
+ /** First line of each reasoning block, throttled. Defaults to `true`. */
145
+ reasoning?: boolean;
146
+ /**
147
+ * Tool calls as they start: `"Running grep, glob…"` (tool names through
148
+ * {@link SlackChannelOptions.toolLabels}), or your own line. Defaults to
149
+ * `true`.
150
+ */
151
+ tools?: boolean | ((calls: readonly SlackToolCall[]) => string);
152
+ /**
153
+ * Rotation Slack cycles through while nothing more specific is known
154
+ * (turn start, long silent stretches). Up to 5 entries. Defaults to
155
+ * Thinking… / Working on it… / Almost there….
156
+ */
157
+ idle?: readonly string[];
158
+ }
159
+
160
+ export type SlackToolCardGroup = "per-call" | "per-tool";
161
+
162
+ /**
163
+ * Task cards for tool calls inside the streamed reply. Slack appends each
164
+ * update's `details` / `output` to the card, so the callbacks return the
165
+ * text to add, not the full card.
166
+ */
167
+ export interface SlackToolCardOptions {
168
+ /**
169
+ * `"per-tool"` (default) keeps one card per tool name and counts calls in
170
+ * the title (`grep ×3`). `"per-call"` shows one card per call.
171
+ */
172
+ group?: SlackToolCardGroup;
173
+ /**
174
+ * `true` (default): cards expand to show one line per call, the way the
175
+ * reasoning card does. `false`: cards are their titles only.
176
+ */
177
+ collapsible?: boolean;
178
+ /**
179
+ * The line a collapsible card shows for a call. Defaults to what the
180
+ * playground shows in that tool's header: the grep pattern, the file
181
+ * path, the shell command.
182
+ */
183
+ details?: (call: SlackToolCall) => string | undefined;
184
+ /** Text appended to a collapsible card when a call finishes. */
185
+ output?: (result: SlackToolResult) => string | undefined;
186
+ }
187
+
188
+ /**
189
+ * One task card that receives the model's reasoning as it streams. Its
190
+ * status is `in_progress` while the model thinks and `complete` once the
191
+ * answer text starts.
192
+ */
193
+ export interface SlackReasoningCardOptions {
194
+ /** Card title. Defaults to `"Thinking"`. */
195
+ title?: string;
196
+ /**
197
+ * Stop appending after this many characters. Defaults to 1500: Slack caps
198
+ * a streamed message (answer text plus cards) near 12k, so a long card
199
+ * shortens the answer that fits before the reply spills into a second
200
+ * message.
201
+ */
202
+ maxChars?: number;
203
+ }
204
+
205
+ /**
206
+ * How the reply reaches the thread while the turn runs.
207
+ *
208
+ * - `"post"` (default): live feedback is the status chip; the answer lands
209
+ * as one message when the turn finishes.
210
+ * - `"stream"`: the answer text streams into one message as it is written.
211
+ * Slack replaces the status chip with its generic "is working…" for the
212
+ * duration, so live feedback inside the message comes from cards.
213
+ */
214
+ export type SlackReplyOptions =
215
+ | {
216
+ mode: "post";
217
+ status?: SlackStatusOptions;
218
+ }
219
+ | {
220
+ mode: "stream";
221
+ status?: SlackStatusOptions;
222
+ /** Defaults to `false`. `true` is `{ group: "per-tool" }`. */
223
+ toolCards?: boolean | SlackToolCardOptions;
224
+ /** Defaults to `false`. */
225
+ reasoningCard?: boolean | SlackReasoningCardOptions;
226
+ };
227
+
228
+ /**
229
+ * Human labels for tool names, shared by the status chip and the cards.
230
+ * A function may return `undefined` to keep the raw tool name.
231
+ */
232
+ export type SlackToolLabels =
233
+ | Readonly<Record<string, string>>
234
+ | ((toolName: string) => string | undefined);
235
+
91
236
  export interface SlackAuthor {
92
237
  /** Slack user id. Empty string for bot posts with no `user` field. */
93
238
  userId: string;
@@ -502,13 +647,21 @@ export interface SlackChannelOptions {
502
647
  */
503
648
  deliverDefaults?: boolean;
504
649
  /**
505
- * Stream assistant text while the turn runs via `chat.startStream` /
506
- * `appendStream` / `stopStream` (defaults to `true`).
650
+ * How the reply reaches the thread: status chip + one message at the end
651
+ * (`"post"`, default), or a live-streamed message with optional tool and
652
+ * reasoning cards (`"stream"`). See {@link SlackReplyOptions}.
653
+ */
654
+ reply?: SlackReplyOptions;
655
+ /** Human labels for tool names in the status chip and cards. */
656
+ toolLabels?: SlackToolLabels;
657
+ /**
658
+ * @deprecated Use `reply: { mode: "stream" }` / `reply: { mode: "post" }`.
659
+ * Ignored when `reply` is set.
507
660
  */
508
661
  streaming?: boolean;
509
662
  /**
510
- * Show task-style thinking steps for tool calls (stream chunks or status).
511
- * Defaults to `true`.
663
+ * @deprecated Use `reply: { mode: "stream", toolCards: true }`. Ignored
664
+ * when `reply` is set.
512
665
  */
513
666
  thinkingSteps?: boolean;
514
667
  /**
@@ -556,8 +709,8 @@ export interface SlackChannelOptions {
556
709
  suggestedPrompts?: SlackSuggestedPrompt[];
557
710
  suggestedPromptsTitle?: string;
558
711
  /**
559
- * Rotating `assistant.threads.setStatus` loading lines (Slack caps each
560
- * entry at 50 chars). Defaults to Thinking… / Working on it… / Almost there….
712
+ * @deprecated Use `reply: { mode, status: { idle: [...] } }`. Ignored when
713
+ * `reply` is set.
561
714
  */
562
715
  loadingMessages?: string[];
563
716
  /**
@@ -51,6 +51,10 @@ import {
51
51
  type FileSinkEntry,
52
52
  type FileSinkPutResult,
53
53
  } from "../files.js";
54
+ import {
55
+ resolveDeploymentStoreSourceIdSync,
56
+ resolveFrameworkStoreSourceIdSync,
57
+ } from "../internal/cursor/hosted-store-secrets.js";
54
58
  import { createAgentStorePresignedUrlValidatorForBackend } from "./agent-store-presigned-url.js";
55
59
 
56
60
  /**
@@ -111,8 +115,7 @@ const MAX_LIST_WALK_PAGES = 1_000;
111
115
  export function isCursorHostedFilesAvailable(
112
116
  env: NodeJS.ProcessEnv = process.env
113
117
  ): boolean {
114
- const sourceId = env[AGENT_SERVE_STORE_SOURCE_ID_ENV_VAR]?.trim();
115
- return sourceId !== undefined && sourceId !== "";
118
+ return resolveDeploymentStoreSourceIdSync(env) !== undefined;
116
119
  }
117
120
 
118
121
  /**
@@ -125,8 +128,7 @@ export function isCursorHostedFilesAvailable(
125
128
  export function cursorFrameworkStoreSourceId(
126
129
  env: NodeJS.ProcessEnv = process.env
127
130
  ): string | undefined {
128
- const sourceId = env[AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID_ENV_VAR]?.trim();
129
- return sourceId === undefined || sourceId === "" ? undefined : sourceId;
131
+ return resolveFrameworkStoreSourceIdSync(env);
130
132
  }
131
133
 
132
134
  /**
@@ -164,6 +166,12 @@ export interface CursorHostedFilesOptions {
164
166
  resolveApiKey?: () => string | undefined;
165
167
  /** File store to address (default: `AGENT_SERVE_STORE_SOURCE_ID`). */
166
168
  storeSourceId?: string;
169
+ /**
170
+ * Lazy store source id, re-resolved on every call and preferred over
171
+ * {@link storeSourceId} — freeze bind drops ids into `/run/cursor/secrets`
172
+ * after serve start without restarting July.
173
+ */
174
+ resolveStoreSourceId?: () => string | undefined;
167
175
  /** Test seam; defaults to global fetch. */
168
176
  fetch?: typeof fetch;
169
177
  }
@@ -650,7 +658,9 @@ function fetchImpl(options: CursorHostedFilesOptions): typeof fetch {
650
658
  */
651
659
  function resolveStoreSourceId(options: CursorHostedFilesOptions): string {
652
660
  const id = (
653
- options.storeSourceId ?? process.env[AGENT_SERVE_STORE_SOURCE_ID_ENV_VAR]
661
+ options.resolveStoreSourceId?.() ??
662
+ options.storeSourceId ??
663
+ resolveDeploymentStoreSourceIdSync(process.env)
654
664
  )?.trim();
655
665
  if (id === undefined || id === "") {
656
666
  throw new Error(
@@ -958,7 +958,7 @@ async function deployAgentsdkApplication(
958
958
  }
959
959
 
960
960
  /**
961
- * `architecture: "v2"` deploy: POST `/internal/agentkit/deployments` and
961
+ * `architecture: "v2"` deploy: POST `/internal/agentsdk/deployments` and
962
962
  * poll the matching GET. The v2 accept path starts freeze bake; it does
963
963
  * not mint an engine alias or alias token.
964
964
  */
@@ -1478,7 +1478,7 @@ export async function cmdStop(
1478
1478
  // deploy). The v2 agentsdk control plane goes first: v1 endpoints refuse
1479
1479
  // `architecture: "v2"` slugs with a 409 rather than a 404, so v1-first
1480
1480
  // could not fall through. A v2 404 (including an older backend without
1481
- // the `/internal/agentkit` mount) falls back to the v1 stop.
1481
+ // the `/internal/agentsdk` mount) falls back to the v1 stop.
1482
1482
  let architecture: "v1" | "v2" = "v2";
1483
1483
  let accepted: StopAccepted | AgentkitStopAccepted;
1484
1484
  try {
@@ -383,18 +383,13 @@ export function selectConversationMirror(input: {
383
383
  return undefined;
384
384
  }
385
385
  const env = input.env ?? process.env;
386
- const frameworkStoreId = cursorFrameworkStoreSourceId(
387
- env as NodeJS.ProcessEnv
388
- );
389
- if (
390
- env[AGENT_SERVE_GENERATION_ENV_VAR] === undefined ||
391
- frameworkStoreId === undefined
392
- ) {
386
+ if (env[AGENT_SERVE_GENERATION_ENV_VAR] === undefined) {
393
387
  return undefined;
394
388
  }
389
+ const envSnapshot = env as NodeJS.ProcessEnv;
395
390
  return createConversationMirror({
396
391
  sink: cursorHostedFiles({
397
- storeSourceId: frameworkStoreId,
392
+ resolveStoreSourceId: () => cursorFrameworkStoreSourceId(envSnapshot),
398
393
  // Full chain (env → key file → stored login): frozen engines listen
399
394
  // unsigned and bind CURSOR_API_KEY_FILE at task time, after this sink
400
395
  // is constructed.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Task-time bind paths for hosted deployment file-store source ids.
3
+ *
4
+ * Freeze v2 snapshots start `agent-serve serve` at bake time without a service
5
+ * account, so neither store id is in process env. Execute-from-freeze binds
6
+ * the deployment credential and store ids into `/run/cursor/secrets/` without
7
+ * restarting July — the same pattern as {@link DEFAULT_CURSOR_API_KEY_FILE}.
8
+ */
9
+
10
+ import { readFileSync } from "node:fs";
11
+
12
+ export const DEFAULT_AGENT_SERVE_STORE_SOURCE_ID_FILE =
13
+ "/run/cursor/secrets/AGENT_SERVE_STORE_SOURCE_ID";
14
+
15
+ export const DEFAULT_AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID_FILE =
16
+ "/run/cursor/secrets/AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID";
17
+
18
+ const STORE_SOURCE_ID_ENV = "AGENT_SERVE_STORE_SOURCE_ID";
19
+ const FRAMEWORK_STORE_SOURCE_ID_ENV = "AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID";
20
+ const STORE_SOURCE_ID_FILE_ENV = "AGENT_SERVE_STORE_SOURCE_ID_FILE";
21
+ const FRAMEWORK_STORE_SOURCE_ID_FILE_ENV =
22
+ "AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID_FILE";
23
+
24
+ function readTrimmedFile(path: string): string | undefined {
25
+ try {
26
+ const raw = readFileSync(path, "utf8").trim();
27
+ return raw === "" ? undefined : raw;
28
+ } catch {
29
+ return undefined;
30
+ }
31
+ }
32
+
33
+ function readBoundSecretFile(
34
+ fileEnvVar: string,
35
+ defaultPath: string,
36
+ env: NodeJS.ProcessEnv
37
+ ): string | undefined {
38
+ const configured = env[fileEnvVar]?.trim();
39
+ const path =
40
+ configured !== undefined && configured !== "" ? configured : defaultPath;
41
+ return readTrimmedFile(path);
42
+ }
43
+
44
+ /** Deployment user-land store: env, then task-bind file. */
45
+ export function resolveDeploymentStoreSourceIdSync(
46
+ env: NodeJS.ProcessEnv = process.env
47
+ ): string | undefined {
48
+ const fromEnv = env[STORE_SOURCE_ID_ENV]?.trim();
49
+ if (fromEnv !== undefined && fromEnv !== "") {
50
+ return fromEnv;
51
+ }
52
+ return readBoundSecretFile(
53
+ STORE_SOURCE_ID_FILE_ENV,
54
+ DEFAULT_AGENT_SERVE_STORE_SOURCE_ID_FILE,
55
+ env
56
+ );
57
+ }
58
+
59
+ /** Engine framework store: env, then task-bind file. */
60
+ export function resolveFrameworkStoreSourceIdSync(
61
+ env: NodeJS.ProcessEnv = process.env
62
+ ): string | undefined {
63
+ const fromEnv = env[FRAMEWORK_STORE_SOURCE_ID_ENV]?.trim();
64
+ if (fromEnv !== undefined && fromEnv !== "") {
65
+ return fromEnv;
66
+ }
67
+ return readBoundSecretFile(
68
+ FRAMEWORK_STORE_SOURCE_ID_FILE_ENV,
69
+ DEFAULT_AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID_FILE,
70
+ env
71
+ );
72
+ }