@cursor/july 0.1.10 → 0.1.11

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 (194) hide show
  1. package/AGENTS.md +1 -0
  2. package/dist/bin/agent-serve.js +17 -3
  3. package/dist/channels/github/github-channel.d.ts.map +1 -1
  4. package/dist/channels/github/github-channel.js +2 -1
  5. package/dist/channels/github/types.d.ts +4 -0
  6. package/dist/channels/github/types.d.ts.map +1 -1
  7. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  8. package/dist/channels/slack/cursor-account.js +1 -1
  9. package/dist/channels/slack/dispatch.d.ts +13 -0
  10. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  11. package/dist/channels/slack/dispatch.js +36 -11
  12. package/dist/channels/slack/eval-directive.d.ts +63 -0
  13. package/dist/channels/slack/eval-directive.d.ts.map +1 -0
  14. package/dist/channels/slack/eval-directive.js +283 -0
  15. package/dist/channels/slack/index.d.ts +1 -0
  16. package/dist/channels/slack/index.d.ts.map +1 -1
  17. package/dist/channels/slack/index.js +1 -0
  18. package/dist/channels/slack/post-update-delivery.js +318 -0
  19. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  20. package/dist/channels/slack/slack-channel.js +4 -2
  21. package/dist/channels/slack/types.d.ts +7 -0
  22. package/dist/channels/slack/types.d.ts.map +1 -1
  23. package/dist/connections.d.ts +10 -5
  24. package/dist/connections.d.ts.map +1 -1
  25. package/dist/connections.js +10 -5
  26. package/dist/docs/404.html +2 -2
  27. package/dist/docs/ab.html +4 -4
  28. package/dist/docs/assets/{app.CY2buaEE.js → app.BcXUGca7.js} +1 -1
  29. package/dist/docs/assets/chunks/@localSearchIndexroot.BIbvK8M1.js +1 -0
  30. package/dist/docs/assets/chunks/{VPLocalSearchBox.Ix954SFJ.js → VPLocalSearchBox.CWur6XGZ.js} +1 -1
  31. package/dist/docs/assets/chunks/{theme.PDwuJbyi.js → theme.BnvulOX2.js} +2 -2
  32. package/dist/docs/assets/{deployment.md.DC5-ImcM.js → deployment.md.TecHo0_2.js} +3 -3
  33. package/dist/docs/assets/{deployment.md.DC5-ImcM.lean.js → deployment.md.TecHo0_2.lean.js} +1 -1
  34. package/dist/docs/assets/guides_mcp-oauth.md.DqmEOQY1.js +29 -0
  35. package/dist/docs/assets/guides_mcp-oauth.md.DqmEOQY1.lean.js +1 -0
  36. package/dist/docs/assets/{index.md.COiu-1jL.js → index.md.t0TM2Qzz.js} +3 -3
  37. package/dist/docs/assets/{index.md.COiu-1jL.lean.js → index.md.t0TM2Qzz.lean.js} +1 -1
  38. package/dist/docs/assets/{reference_cli.md.CjfiV2Q-.js → reference_cli.md.B74NrVzb.js} +3 -3
  39. package/dist/docs/assets/{reference_cli.md.CjfiV2Q-.lean.js → reference_cli.md.B74NrVzb.lean.js} +1 -1
  40. package/dist/docs/assets/{reference_connections.md.FbWupyzW.js → reference_connections.md.CsSr3rDz.js} +8 -3
  41. package/dist/docs/assets/{reference_connections.md.FbWupyzW.lean.js → reference_connections.md.CsSr3rDz.lean.js} +1 -1
  42. package/dist/docs/assets/reference_prompt.md.BaiweQxE.js +1 -0
  43. package/dist/docs/assets/reference_prompt.md.BaiweQxE.lean.js +1 -0
  44. package/dist/docs/assets/{reference_tools.md.CqgJroI0.js → reference_tools.md.BRSDnTbN.js} +16 -3
  45. package/dist/docs/assets/{reference_tools.md.CqgJroI0.lean.js → reference_tools.md.BRSDnTbN.lean.js} +1 -1
  46. package/dist/docs/assets/{troubleshooting.md.DkovjS7h.js → troubleshooting.md.CmQkmnzC.js} +1 -1
  47. package/dist/docs/assets/{troubleshooting.md.DkovjS7h.lean.js → troubleshooting.md.CmQkmnzC.lean.js} +1 -1
  48. package/dist/docs/building-with-agents.html +4 -4
  49. package/dist/docs/concepts.html +4 -4
  50. package/dist/docs/deployment.html +7 -7
  51. package/dist/docs/evals.html +4 -4
  52. package/dist/docs/example-agents/approval-buddy.html +4 -4
  53. package/dist/docs/example-agents/benny.html +4 -4
  54. package/dist/docs/example-agents/bugbot.html +4 -4
  55. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  56. package/dist/docs/example-agents/codeowners-review.html +4 -4
  57. package/dist/docs/example-agents/concierge.html +4 -4
  58. package/dist/docs/example-agents/fsd.html +4 -4
  59. package/dist/docs/example-agents/index.html +4 -4
  60. package/dist/docs/example-agents/knowledge-base.html +4 -4
  61. package/dist/docs/example-agents/oncall.html +4 -4
  62. package/dist/docs/example-agents/security-reviewer.html +4 -4
  63. package/dist/docs/example-agents/slack-agent.html +4 -4
  64. package/dist/docs/example-agents/weather-agent.html +4 -4
  65. package/dist/docs/guides/agent-to-agent.html +4 -4
  66. package/dist/docs/guides/cloud-runtime.html +4 -4
  67. package/dist/docs/guides/github.html +4 -4
  68. package/dist/docs/guides/human-in-the-loop.html +4 -4
  69. package/dist/docs/guides/mcp-oauth.html +53 -0
  70. package/dist/docs/guides/slack.html +4 -4
  71. package/dist/docs/guides/webhooks.html +4 -4
  72. package/dist/docs/hashmap.json +1 -1
  73. package/dist/docs/hillclimbing.html +4 -4
  74. package/dist/docs/index.html +6 -6
  75. package/dist/docs/quickstart.html +4 -4
  76. package/dist/docs/reference/agent-config.html +4 -4
  77. package/dist/docs/reference/channels.html +4 -4
  78. package/dist/docs/reference/cli.html +6 -6
  79. package/dist/docs/reference/connections.html +12 -7
  80. package/dist/docs/reference/hooks.html +4 -4
  81. package/dist/docs/reference/http-api.html +4 -4
  82. package/dist/docs/reference/instructions.html +4 -4
  83. package/dist/docs/reference/playground.html +4 -4
  84. package/dist/docs/reference/project-layout.html +4 -4
  85. package/dist/docs/reference/prompt.html +33 -0
  86. package/dist/docs/reference/schedules.html +4 -4
  87. package/dist/docs/reference/sessions.html +4 -4
  88. package/dist/docs/reference/skills.html +5 -5
  89. package/dist/docs/reference/subagents.html +4 -4
  90. package/dist/docs/reference/tools.html +20 -7
  91. package/dist/docs/scaffolding-agents.html +4 -4
  92. package/dist/docs/storage.html +4 -4
  93. package/dist/docs/troubleshooting.html +5 -5
  94. package/dist/evals.d.ts +39 -0
  95. package/dist/evals.d.ts.map +1 -1
  96. package/dist/index.d.ts +3 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +2 -1
  99. package/dist/internal/cli-mcp-oauth.d.ts +16 -0
  100. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -0
  101. package/dist/internal/cli-mcp-oauth.js +194 -0
  102. package/dist/internal/cli-mcp.d.ts +4 -1
  103. package/dist/internal/cli-mcp.d.ts.map +1 -1
  104. package/dist/internal/cli-mcp.js +19 -1
  105. package/dist/internal/corp-ca.d.ts +9 -0
  106. package/dist/internal/corp-ca.d.ts.map +1 -0
  107. package/dist/internal/corp-ca.js +44 -0
  108. package/dist/internal/cursor/account-mcp.d.ts +17 -4
  109. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  110. package/dist/internal/cursor/account-mcp.js +105 -17
  111. package/dist/internal/discovery.d.ts.map +1 -1
  112. package/dist/internal/discovery.js +64 -10
  113. package/dist/internal/distribution.d.ts.map +1 -1
  114. package/dist/internal/distribution.js +1 -0
  115. package/dist/internal/json-dir-store.js +100 -0
  116. package/dist/internal/logs-client.js +1 -1
  117. package/dist/internal/mcp-host.d.ts.map +1 -1
  118. package/dist/internal/mcp-host.js +49 -26
  119. package/dist/internal/mcp-oauth.d.ts +88 -0
  120. package/dist/internal/mcp-oauth.d.ts.map +1 -0
  121. package/dist/internal/mcp-oauth.js +385 -0
  122. package/dist/internal/resolved-connections.d.ts.map +1 -1
  123. package/dist/internal/resolved-connections.js +4 -1
  124. package/dist/internal/sdk-runner.d.ts +9 -0
  125. package/dist/internal/sdk-runner.d.ts.map +1 -1
  126. package/dist/internal/sdk-runner.js +28 -2
  127. package/dist/internal/server.d.ts.map +1 -1
  128. package/dist/internal/server.js +13 -5
  129. package/dist/internal/session-engine.d.ts +7 -9
  130. package/dist/internal/session-engine.d.ts.map +1 -1
  131. package/dist/internal/session-engine.js +96 -64
  132. package/dist/internal/sessions-client.d.ts.map +1 -1
  133. package/dist/internal/sessions-client.js +2 -1
  134. package/dist/internal/stream-progress.d.ts.map +1 -1
  135. package/dist/internal/stream-progress.js +8 -4
  136. package/dist/internal/turn-governor.d.ts +54 -0
  137. package/dist/internal/turn-governor.d.ts.map +1 -0
  138. package/dist/internal/turn-governor.js +111 -0
  139. package/dist/playground/assets/{index-C0-QEG4O.js → index-BIGLubwK.js} +46 -46
  140. package/dist/playground/assets/{index-z3JY9O2x.css → index-C17ZQKDG.css} +1 -1
  141. package/dist/playground/index.html +2 -2
  142. package/dist/prompt.d.ts +39 -0
  143. package/dist/prompt.d.ts.map +1 -0
  144. package/dist/prompt.js +70 -0
  145. package/dist/types.d.ts +78 -5
  146. package/dist/types.d.ts.map +1 -1
  147. package/dist/types.js +6 -0
  148. package/docs/.vitepress/cache/deps/package.json +3 -0
  149. package/docs/README.md +4 -0
  150. package/docs/deployment.md +6 -1
  151. package/docs/guides/mcp-oauth.md +157 -0
  152. package/docs/reference/cli.md +28 -0
  153. package/docs/reference/connections.md +28 -0
  154. package/docs/reference/prompt.md +47 -0
  155. package/docs/reference/tools.md +21 -0
  156. package/docs/troubleshooting.md +11 -0
  157. package/package.json +9 -2
  158. package/skills/create-agent/SKILL.md +7 -3
  159. package/skills/framework-map/SKILL.md +1 -0
  160. package/skills/mcp-auth/SKILL.md +138 -0
  161. package/src/bin/agent-serve.ts +20 -3
  162. package/src/channels/github/github-channel.ts +2 -0
  163. package/src/channels/github/types.ts +4 -0
  164. package/src/channels/slack/cursor-account.ts +2 -0
  165. package/src/channels/slack/dispatch.ts +49 -0
  166. package/src/channels/slack/eval-directive.ts +348 -0
  167. package/src/channels/slack/index.ts +1 -0
  168. package/src/channels/slack/slack-channel.ts +3 -0
  169. package/src/channels/slack/types.ts +7 -0
  170. package/src/connections.ts +10 -5
  171. package/src/evals.ts +43 -0
  172. package/src/index.ts +3 -0
  173. package/src/internal/cli-mcp-oauth.ts +254 -0
  174. package/src/internal/cli-mcp.ts +25 -2
  175. package/src/internal/cursor/account-mcp.ts +153 -41
  176. package/src/internal/discovery.ts +70 -8
  177. package/src/internal/distribution.ts +1 -0
  178. package/src/internal/logs-client.ts +1 -1
  179. package/src/internal/mcp-host.ts +33 -9
  180. package/src/internal/mcp-oauth.ts +496 -0
  181. package/src/internal/resolved-connections.ts +6 -1
  182. package/src/internal/sdk-runner.ts +37 -1
  183. package/src/internal/server.ts +13 -5
  184. package/src/internal/session-engine.ts +115 -74
  185. package/src/internal/sessions-client.ts +2 -1
  186. package/src/internal/stream-progress.ts +8 -0
  187. package/src/internal/turn-governor.ts +145 -0
  188. package/src/prompt.ts +83 -0
  189. package/src/types.ts +76 -6
  190. package/dist/docs/assets/chunks/@localSearchIndexroot.OfDZ5CXg.js +0 -1
  191. package/dist/internal/persistence-coordinator.d.ts +0 -127
  192. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  193. package/dist/persistence.d.ts +0 -184
  194. package/dist/persistence.d.ts.map +0 -1
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Built-in Slack `eval` directive — run playground evals from a mention/DM
3
+ * without per-agent custom handlers (parity with CLI / playground runs).
4
+ *
5
+ * Examples (after stripping @mentions / cursorAccount agent name):
6
+ * eval
7
+ * eval triage/open-incidents smoke
8
+ * eval --tag oncall
9
+ * eval list
10
+ * eval status
11
+ * eval help
12
+ */
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ import { EvalRunConflictError, EvalRunEmptyError, } from "../../internal/eval-run-store.js";
23
+ import { stripLeadingSlackMentions } from "./stop.js";
24
+ const EVAL_POLL_MS = 2000;
25
+ const TYPING_STATUS_MAX = 50;
26
+ function escapeRegExp(value) {
27
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
+ }
29
+ /**
30
+ * Normalize inbound Slack text for directive parsing: strip leading
31
+ * `<@U…>` mentions, leading `@U…` markdown mentions, and an optional
32
+ * cursorAccount agentName prefix (`OncallBuddy eval …`).
33
+ */
34
+ export function normalizeSlackEvalDirectiveBody(text, options) {
35
+ var _a;
36
+ let body = stripLeadingSlackMentions(text);
37
+ // markdown form from slackTextToMarkdown / cursorAccount envelopes
38
+ while (/^@[A-Za-z0-9._-]+\s+/.test(body)) {
39
+ body = body.replace(/^@[A-Za-z0-9._-]+\s+/, "").trim();
40
+ }
41
+ const agentName = (_a = options === null || options === void 0 ? void 0 : options.agentName) === null || _a === void 0 ? void 0 : _a.trim();
42
+ if (agentName !== undefined && agentName.length > 0) {
43
+ const prefix = new RegExp(`^${escapeRegExp(agentName)}\\s+`, "i");
44
+ body = body.replace(prefix, "").trim();
45
+ }
46
+ return body;
47
+ }
48
+ /**
49
+ * Parse a Slack eval directive. Returns null when the message is not an
50
+ * eval command (falls through to the normal agent handler).
51
+ */
52
+ export function parseSlackEvalDirective(text, options) {
53
+ var _a, _b, _c;
54
+ const body = normalizeSlackEvalDirectiveBody(text, options);
55
+ if (body.length === 0) {
56
+ return null;
57
+ }
58
+ const tokens = body.split(/\s+/).filter((t) => t.length > 0);
59
+ if (tokens.length === 0 || !/^eval$/i.test((_a = tokens[0]) !== null && _a !== void 0 ? _a : "")) {
60
+ return null;
61
+ }
62
+ const rest = tokens.slice(1);
63
+ if (rest.length === 0) {
64
+ return { kind: "run" };
65
+ }
66
+ const head = ((_b = rest[0]) !== null && _b !== void 0 ? _b : "").toLowerCase();
67
+ if (rest.length === 1 &&
68
+ (head === "help" || head === "-h" || head === "--help")) {
69
+ return { kind: "help" };
70
+ }
71
+ if (rest.length === 1 && (head === "list" || head === "ls")) {
72
+ return { kind: "list" };
73
+ }
74
+ if (rest.length === 1 && head === "status") {
75
+ return { kind: "status" };
76
+ }
77
+ const filterIds = [];
78
+ const tags = [];
79
+ for (let i = 0; i < rest.length; i++) {
80
+ const tok = (_c = rest[i]) !== null && _c !== void 0 ? _c : "";
81
+ if (tok === "--tag" || tok === "-t") {
82
+ const value = rest[i + 1];
83
+ if (value === undefined || value.length === 0) {
84
+ return { kind: "help" };
85
+ }
86
+ tags.push(value);
87
+ i += 1;
88
+ continue;
89
+ }
90
+ if (tok.startsWith("--tag=")) {
91
+ const value = tok.slice("--tag=".length);
92
+ if (value.length === 0) {
93
+ return { kind: "help" };
94
+ }
95
+ tags.push(value);
96
+ continue;
97
+ }
98
+ if (tok.startsWith("-t=") && tok.length > 3) {
99
+ tags.push(tok.slice(3));
100
+ continue;
101
+ }
102
+ if (tok.startsWith("-")) {
103
+ // Unknown flag — surface help rather than treating as an eval id.
104
+ return { kind: "help" };
105
+ }
106
+ filterIds.push(tok);
107
+ }
108
+ const run = { kind: "run" };
109
+ if (filterIds.length > 0) {
110
+ run.filterIds = filterIds;
111
+ }
112
+ if (tags.length > 0) {
113
+ run.tags = tags;
114
+ }
115
+ return run;
116
+ }
117
+ /** True when either raw Slack text or markdown form is an eval directive. */
118
+ export function isSlackEvalDirective(text, markdown, options) {
119
+ return (parseSlackEvalDirective(text, options) !== null ||
120
+ parseSlackEvalDirective(markdown, options) !== null);
121
+ }
122
+ export function formatSlackEvalHelp() {
123
+ return [
124
+ "*Eval commands* (same batches as the playground / `agentkit eval --prod`):",
125
+ "• `eval` — run all evals",
126
+ "• `eval <id> [id…]` — run specific eval ids",
127
+ "• `eval --tag <tag>` — run evals with a tag (`-t` also works)",
128
+ "• `eval list` — list available evals",
129
+ "• `eval status` — show the active or latest eval run",
130
+ "• `eval help` — this message",
131
+ ].join("\n");
132
+ }
133
+ export function formatSlackEvalList(evals) {
134
+ if (evals.length === 0) {
135
+ return "No evals found under `evals/`.";
136
+ }
137
+ const lines = [`*${evals.length} eval(s):*`];
138
+ for (const item of evals) {
139
+ const tags = item.tags === undefined || item.tags.length === 0
140
+ ? ""
141
+ : ` _[${item.tags.join(", ")}]_`;
142
+ const desc = item.description === undefined || item.description === ""
143
+ ? ""
144
+ : ` — ${item.description}`;
145
+ lines.push(`• \`${item.id}\`${tags}${desc}`);
146
+ }
147
+ return lines.join("\n");
148
+ }
149
+ export function formatSlackEvalRunSummary(run) {
150
+ const { summary } = run;
151
+ const statusLabel = run.status === "completed"
152
+ ? "completed"
153
+ : run.status === "failed"
154
+ ? "failed"
155
+ : "running";
156
+ const lines = [
157
+ `*Eval run* \`${run.runId}\` — *${statusLabel}*`,
158
+ `• ${summary.passed} passed, ${summary.failed} failed, ${summary.total} total` +
159
+ (summary.done < summary.total
160
+ ? ` (${summary.done}/${summary.total} done)`
161
+ : ""),
162
+ ];
163
+ if (run.error !== undefined && run.error !== "") {
164
+ lines.push(`• error: ${run.error}`);
165
+ }
166
+ for (const c of run.cases) {
167
+ const mark = c.status !== "done"
168
+ ? c.status === "running"
169
+ ? "RUN "
170
+ : "…. "
171
+ : c.ok === true
172
+ ? "PASS"
173
+ : "FAIL";
174
+ const dur = c.durationMs === undefined
175
+ ? ""
176
+ : ` (${(c.durationMs / 1000).toFixed(1)}s)`;
177
+ const err = c.ok === false && c.error !== undefined && c.error !== ""
178
+ ? ` — ${c.error}`
179
+ : "";
180
+ lines.push(`\`${mark}\` \`${c.id}\`${dur}${err}`);
181
+ }
182
+ return lines.join("\n");
183
+ }
184
+ function truncateTyping(status) {
185
+ if (status.length <= TYPING_STATUS_MAX) {
186
+ return status;
187
+ }
188
+ return `${status.slice(0, TYPING_STATUS_MAX - 1)}…`;
189
+ }
190
+ function sleep(ms) {
191
+ return __awaiter(this, void 0, void 0, function* () {
192
+ yield new Promise((resolve) => {
193
+ setTimeout(resolve, ms);
194
+ });
195
+ });
196
+ }
197
+ function safeTyping(binding, status) {
198
+ return __awaiter(this, void 0, void 0, function* () {
199
+ try {
200
+ yield binding.thread.startTyping(truncateTyping(status));
201
+ }
202
+ catch (_a) {
203
+ // best-effort status
204
+ }
205
+ });
206
+ }
207
+ function safePost(binding, text) {
208
+ return __awaiter(this, void 0, void 0, function* () {
209
+ try {
210
+ yield binding.thread.post(text);
211
+ }
212
+ catch (_a) {
213
+ // best-effort reply
214
+ }
215
+ });
216
+ }
217
+ /**
218
+ * Handle a parsed Slack eval directive against {@link HostEvalsApi}.
219
+ * Posts results in-thread; does not start an agent turn.
220
+ */
221
+ export function handleSlackEvalDirective(args) {
222
+ return __awaiter(this, void 0, void 0, function* () {
223
+ var _a, _b;
224
+ const { directive, evals, binding } = args;
225
+ const pollMs = (_a = args.pollMs) !== null && _a !== void 0 ? _a : EVAL_POLL_MS;
226
+ if (directive.kind === "help") {
227
+ yield safePost(binding, formatSlackEvalHelp());
228
+ return;
229
+ }
230
+ if (directive.kind === "list") {
231
+ const listed = yield evals.list();
232
+ yield safePost(binding, formatSlackEvalList(listed.evals));
233
+ return;
234
+ }
235
+ if (directive.kind === "status") {
236
+ const { runs, activeRunId } = yield evals.listRunsWithActive();
237
+ const run = (_b = (activeRunId === undefined ? undefined : yield evals.get(activeRunId))) !== null && _b !== void 0 ? _b : runs[0];
238
+ if (run === undefined) {
239
+ yield safePost(binding, "No eval runs yet. Try `eval list` then `eval`.");
240
+ return;
241
+ }
242
+ yield safePost(binding, formatSlackEvalRunSummary(run));
243
+ return;
244
+ }
245
+ yield safeTyping(binding, "Starting evals…");
246
+ let run;
247
+ try {
248
+ run = yield evals.start({
249
+ filterIds: directive.filterIds,
250
+ tags: directive.tags,
251
+ });
252
+ }
253
+ catch (error) {
254
+ if (error instanceof EvalRunConflictError) {
255
+ const active = yield evals.get(error.activeRunId);
256
+ yield safePost(binding, active === undefined
257
+ ? `An eval run is already in progress (\`${error.activeRunId}\`).`
258
+ : `An eval run is already in progress:\n${formatSlackEvalRunSummary(active)}`);
259
+ return;
260
+ }
261
+ if (error instanceof EvalRunEmptyError) {
262
+ yield safePost(binding, "No matching evals. Try `eval list` or `eval help`.");
263
+ return;
264
+ }
265
+ const message = error instanceof Error ? error.message : String(error);
266
+ yield safePost(binding, `Failed to start evals: ${message}`);
267
+ return;
268
+ }
269
+ yield safePost(binding, `Started eval run \`${run.runId}\` (${run.summary.total} case(s)).`);
270
+ while (run.status === "running") {
271
+ yield safeTyping(binding, `Evals ${run.summary.done}/${run.summary.total}…`);
272
+ yield sleep(pollMs);
273
+ const next = yield evals.get(run.runId);
274
+ if (next === undefined) {
275
+ yield safePost(binding, `Eval run \`${run.runId}\` disappeared before completion.`);
276
+ return;
277
+ }
278
+ run = next;
279
+ }
280
+ yield safePost(binding, formatSlackEvalRunSummary(run));
281
+ yield safeTyping(binding, "Evals done.");
282
+ });
283
+ }
@@ -28,6 +28,7 @@ export * from "./cursor-account.js";
28
28
  export * from "./defaults.js";
29
29
  export * from "./dispatch.js";
30
30
  export * from "./doctor.js";
31
+ export * from "./eval-directive.js";
31
32
  export * from "./event-id-dedupe.js";
32
33
  export * from "./inbound.js";
33
34
  export * from "./init.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC"}
@@ -28,6 +28,7 @@ export * from "./cursor-account.js";
28
28
  export * from "./defaults.js";
29
29
  export * from "./dispatch.js";
30
30
  export * from "./doctor.js";
31
+ export * from "./eval-directive.js";
31
32
  export * from "./event-id-dedupe.js";
32
33
  export * from "./inbound.js";
33
34
  export * from "./init.js";
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Live-reply delivery strategies for the Slack default handlers.
3
+ *
4
+ * A single strategy owns how an in-progress reply is opened, updated,
5
+ * finalized, and cleaned up so the handlers in `defaults.ts` don't fork on the
6
+ * delivery mode at every event:
7
+ *
8
+ * - `stream`: Slack chat.startStream / appendStream / stopStream.
9
+ * - `post-update`: a chat.postMessage skeleton edited with chat.update. This is
10
+ * the only path that preserves Cursor-account identity pinning (which only
11
+ * covers postMessage).
12
+ *
13
+ * `buildDefaultEvents` selects one via {@link selectLiveDeliveryStrategy} up
14
+ * front and calls into it. All calls keep the same soft-fail / log behavior as
15
+ * the inlined logic they replaced.
16
+ */
17
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
+ return new (P || (P = Promise))(function (resolve, reject) {
20
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
24
+ });
25
+ };
26
+ import { appendChatStream, deleteMessage, postMessage, setThreadStatus, startChatStream, stopChatStream, updateMessage, } from "./api.js";
27
+ import { SLACK_POST_UPDATE_MIN_INTERVAL_MS } from "./constants.js";
28
+ import { computeStreamAppend } from "./stream.js";
29
+ function createPostUpdateStrategy(deps) {
30
+ const { botToken, getLog } = deps;
31
+ return {
32
+ inlineToolSteps: false,
33
+ start(_a) {
34
+ return __awaiter(this, arguments, void 0, function* ({ channel, channelId, threadTs, sessionId }) {
35
+ const log = getLog();
36
+ try {
37
+ const posted = yield postMessage({
38
+ botToken,
39
+ channel: channelId,
40
+ text: "…",
41
+ threadTs,
42
+ });
43
+ if (posted.ts !== "") {
44
+ channel.state.streamTs = posted.ts;
45
+ channel.state.streamChannelId = channelId;
46
+ channel.state.lastPostedText = "";
47
+ log.info("postMessage skeleton ok", {
48
+ channel: channelId,
49
+ message_ts: posted.ts,
50
+ session: sessionId,
51
+ });
52
+ }
53
+ }
54
+ catch (error) {
55
+ log.warn("postMessage skeleton failed", {
56
+ error: error instanceof Error ? error.message : String(error),
57
+ channel: channelId,
58
+ session: sessionId,
59
+ });
60
+ // Transient proxy/network failures must not permanently disable
61
+ // live delivery — only the stream path sets streamingUnavailable
62
+ // when Slack reports the feature unsupported.
63
+ }
64
+ });
65
+ },
66
+ append(_a) {
67
+ return __awaiter(this, arguments, void 0, function* ({ channel, channelId, streamTs, text, sessionId }) {
68
+ var _b;
69
+ const now = Date.now();
70
+ const isFirstUpdate = channel.state.lastPostedText === null ||
71
+ channel.state.lastPostedText === "";
72
+ const lastAt = (_b = channel.state.lastPostUpdateAtMs) !== null && _b !== void 0 ? _b : 0;
73
+ const due = isFirstUpdate || now - lastAt >= SLACK_POST_UPDATE_MIN_INTERVAL_MS;
74
+ if (!due) {
75
+ // Skip intermediate update; message.completed flushes full text.
76
+ return;
77
+ }
78
+ const result = yield updateMessage({
79
+ botToken,
80
+ channel: channelId,
81
+ ts: streamTs,
82
+ text,
83
+ });
84
+ if (!result.ok) {
85
+ getLog().warn("chat.update failed", {
86
+ error: result.error,
87
+ channel: channelId,
88
+ message_ts: streamTs,
89
+ session: sessionId,
90
+ });
91
+ return;
92
+ }
93
+ channel.state.lastPostUpdateAtMs = now;
94
+ channel.state.lastPostedText = text;
95
+ });
96
+ },
97
+ finalize(_a) {
98
+ return __awaiter(this, arguments, void 0, function* ({ channel, channelId, streamTs, text, sessionId }) {
99
+ const log = getLog();
100
+ const result = yield updateMessage({
101
+ botToken,
102
+ channel: channelId,
103
+ ts: streamTs,
104
+ text,
105
+ });
106
+ channel.state.streamTs = null;
107
+ if (result.ok) {
108
+ channel.state.lastPostedText = text;
109
+ log.info("reply delivered via chat.update", {
110
+ channel: channelId,
111
+ message_ts: streamTs,
112
+ session: sessionId,
113
+ chars: text.length,
114
+ });
115
+ return { delivered: true };
116
+ }
117
+ log.warn("chat.update finalize failed; falling back to postMessage", {
118
+ error: result.error,
119
+ channel: channelId,
120
+ message_ts: streamTs,
121
+ session: sessionId,
122
+ });
123
+ // Only suppress the fallback when the skeleton already holds the exact
124
+ // final text. Partial updates must not permanently truncate.
125
+ if (channel.state.lastPostedText === text) {
126
+ return { delivered: true };
127
+ }
128
+ // The skeleton still shows the placeholder or a stale partial and we're
129
+ // about to post the full reply as a fresh message. Best-effort delete the
130
+ // orphan so the thread doesn't show a duplicate.
131
+ yield deleteMessage({ botToken, channel: channelId, ts: streamTs });
132
+ return { delivered: false };
133
+ });
134
+ },
135
+ finalizeEmpty(_a) {
136
+ return __awaiter(this, arguments, void 0, function* ({ channelId, streamTs }) {
137
+ yield updateMessage({
138
+ botToken,
139
+ channel: channelId,
140
+ ts: streamTs,
141
+ text: " ",
142
+ });
143
+ });
144
+ },
145
+ cleanupOrphan(_a) {
146
+ return __awaiter(this, arguments, void 0, function* ({ channel }) {
147
+ var _b;
148
+ // A skeleton that was never finalized (e.g. an empty turn that produced
149
+ // no message.completed) would otherwise linger as a "…" placeholder
150
+ // forever. Best-effort delete it and clear typing.
151
+ const orphanTs = channel.state.streamTs;
152
+ if (orphanTs === null || orphanTs === "") {
153
+ return;
154
+ }
155
+ const orphanChannelId = (_b = channel.state.streamChannelId) !== null && _b !== void 0 ? _b : channel.state.channelId;
156
+ if (orphanChannelId !== null && orphanChannelId !== "") {
157
+ yield deleteMessage({
158
+ botToken,
159
+ channel: orphanChannelId,
160
+ ts: orphanTs,
161
+ });
162
+ }
163
+ channel.state.streamTs = null;
164
+ if (channel.state.channelId !== null && channel.state.threadTs !== null) {
165
+ yield setThreadStatus({
166
+ botToken,
167
+ channelId: channel.state.channelId,
168
+ threadTs: channel.state.threadTs,
169
+ status: "",
170
+ });
171
+ }
172
+ });
173
+ },
174
+ fail(_a) {
175
+ return __awaiter(this, arguments, void 0, function* ({ channel, channelId, streamTs, errorText }) {
176
+ const updated = yield updateMessage({
177
+ botToken,
178
+ channel: channelId,
179
+ ts: streamTs,
180
+ text: errorText,
181
+ });
182
+ channel.state.streamTs = null;
183
+ return { delivered: updated.ok };
184
+ });
185
+ },
186
+ };
187
+ }
188
+ function createStreamStrategy(deps) {
189
+ const { botToken, getLog } = deps;
190
+ return {
191
+ inlineToolSteps: true,
192
+ start(_a) {
193
+ return __awaiter(this, arguments, void 0, function* ({ channel, channelId, threadTs, sessionId }) {
194
+ const log = getLog();
195
+ const started = yield startChatStream({
196
+ botToken,
197
+ channel: channelId,
198
+ thread_ts: threadTs,
199
+ markdown_text: "",
200
+ });
201
+ if (!started.ok) {
202
+ log.warn("chat.startStream unavailable", {
203
+ error: started.error,
204
+ channel: channelId,
205
+ thread_ts: threadTs,
206
+ session: sessionId,
207
+ });
208
+ if (started.unsupported === true) {
209
+ channel.state.streamingUnavailable = true;
210
+ }
211
+ return;
212
+ }
213
+ channel.state.streamTs = started.ts;
214
+ channel.state.streamChannelId = started.channel;
215
+ channel.state.lastPostedText = "";
216
+ log.info("chat.startStream ok", {
217
+ channel: started.channel,
218
+ stream_ts: started.ts,
219
+ session: sessionId,
220
+ });
221
+ });
222
+ },
223
+ append(_a) {
224
+ return __awaiter(this, arguments, void 0, function* ({ channel, channelId, streamTs, text, sessionId }) {
225
+ const appendText = computeStreamAppend(channel.state.lastPostedText, text);
226
+ if (appendText === "") {
227
+ return;
228
+ }
229
+ const result = yield appendChatStream({
230
+ botToken,
231
+ channel: channelId,
232
+ ts: streamTs,
233
+ markdown_text: appendText,
234
+ });
235
+ if (!result.ok) {
236
+ getLog().warn("appendStream failed", {
237
+ error: result.error,
238
+ channel: channelId,
239
+ stream_ts: streamTs,
240
+ session: sessionId,
241
+ });
242
+ if (result.unsupported === true) {
243
+ channel.state.streamingUnavailable = true;
244
+ }
245
+ return;
246
+ }
247
+ channel.state.lastPostedText = text;
248
+ });
249
+ },
250
+ finalize(_a) {
251
+ return __awaiter(this, arguments, void 0, function* ({ channel, channelId, streamTs, text, sessionId }) {
252
+ const log = getLog();
253
+ // Only send the unsent suffix — full text was already appended.
254
+ const appendText = computeStreamAppend(channel.state.lastPostedText, text);
255
+ const stopped = yield stopChatStream({
256
+ botToken,
257
+ channel: channelId,
258
+ ts: streamTs,
259
+ markdown_text: appendText === "" ? undefined : appendText,
260
+ });
261
+ if (stopped.ok) {
262
+ channel.state.streamTs = null;
263
+ channel.state.lastPostedText = text;
264
+ log.info("reply delivered via stream", {
265
+ channel: channelId,
266
+ stream_ts: streamTs,
267
+ session: sessionId,
268
+ chars: text.length,
269
+ });
270
+ return { delivered: true };
271
+ }
272
+ log.warn("chat.stopStream failed; falling back to postMessage", {
273
+ error: stopped.error,
274
+ channel: channelId,
275
+ stream_ts: streamTs,
276
+ session: sessionId,
277
+ });
278
+ if (stopped.unsupported === true) {
279
+ channel.state.streamingUnavailable = true;
280
+ }
281
+ channel.state.streamTs = null;
282
+ // If we already streamed content, do not post the full reply again.
283
+ const alreadyStreamed = typeof channel.state.lastPostedText === "string" &&
284
+ channel.state.lastPostedText.length > 0;
285
+ return { delivered: alreadyStreamed };
286
+ });
287
+ },
288
+ finalizeEmpty(_a) {
289
+ return __awaiter(this, arguments, void 0, function* ({ channelId, streamTs }) {
290
+ yield stopChatStream({ botToken, channel: channelId, ts: streamTs });
291
+ });
292
+ },
293
+ cleanupOrphan() {
294
+ return __awaiter(this, void 0, void 0, function* () {
295
+ // The stream path closes its message in message.completed / turn.failed;
296
+ // there is nothing to clean up after turn.completed.
297
+ });
298
+ },
299
+ fail(_a) {
300
+ return __awaiter(this, arguments, void 0, function* ({ channel, channelId, streamTs, errorText }) {
301
+ const stopped = yield stopChatStream({
302
+ botToken,
303
+ channel: channelId,
304
+ ts: streamTs,
305
+ markdown_text: errorText,
306
+ });
307
+ channel.state.streamTs = null;
308
+ return { delivered: stopped.ok };
309
+ });
310
+ },
311
+ };
312
+ }
313
+ /** Pick the live-reply delivery strategy for a `buildDefaultEvents` instance. */
314
+ export function selectLiveDeliveryStrategy(deps, usePostUpdate) {
315
+ return usePostUpdate
316
+ ? createPostUpdateStrategy(deps)
317
+ : createStreamStrategy(deps);
318
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"slack-channel.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/slack-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,iBAAiB,EAMlB,MAAM,gBAAgB,CAAC;AAsCxB,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAC7D,OAAO,GAAE,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,GAAG;IAChD,SAAS,CAAC,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC;CACnC,GACL,iBAAiB,CAAC,iBAAiB,CAAC,CAqYtC"}
1
+ {"version":3,"file":"slack-channel.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/slack-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,iBAAiB,EAMlB,MAAM,gBAAgB,CAAC;AAsCxB,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAC7D,OAAO,GAAE,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,GAAG;IAChD,SAAS,CAAC,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC;CACnC,GACL,iBAAiB,CAAC,iBAAiB,CAAC,CAwYtC"}
@@ -90,6 +90,7 @@ export function slackChannel(options = {}) {
90
90
  const engagement = (_h = options.engagement) !== null && _h !== void 0 ? _h : {};
91
91
  const mentionsEnabled = engagement.mentions !== false;
92
92
  const directMessagesEnabled = engagement.directMessages !== false;
93
+ const evalCommandsEnabled = options.evalCommands !== false;
93
94
  const channelPosts = engagement.channelPosts;
94
95
  if (channelPosts !== undefined) {
95
96
  const entries = channelPosts.allow.filter((entry) => entry.trim() !== "");
@@ -130,7 +131,8 @@ export function slackChannel(options = {}) {
130
131
  onDirectMessage,
131
132
  credentials,
132
133
  eventIds, host: args.host, log: getLog(), mentionsEnabled,
133
- directMessagesEnabled }, (args.hasContinuationSession === undefined
134
+ directMessagesEnabled,
135
+ evalCommandsEnabled }, (args.hasContinuationSession === undefined
134
136
  ? {}
135
137
  : { hasContinuationSession: args.hasContinuationSession })), (args.isContinuationBusy === undefined
136
138
  ? {}
@@ -238,7 +240,7 @@ export function slackChannel(options = {}) {
238
240
  getBotId: () => botId,
239
241
  log: getLog(),
240
242
  dispatch: (message, eventId) => {
241
- args.waitUntil(dispatchInboundMessage(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ kind: "channel_post", message, handler: onChannelPost, send: args.send, credentials, host: args.host, log: getLog() }, (args.hasContinuationSession === undefined
243
+ args.waitUntil(dispatchInboundMessage(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ kind: "channel_post", message, handler: onChannelPost, send: args.send, credentials, host: args.host, log: getLog(), evalCommandsEnabled }, (args.hasContinuationSession === undefined
242
244
  ? {}
243
245
  : {
244
246
  hasContinuationSession: args.hasContinuationSession,
@@ -332,6 +332,13 @@ export interface SlackChannelOptions {
332
332
  * entry at 50 chars). Defaults to Thinking… / Working on it… / Almost there….
333
333
  */
334
334
  loadingMessages?: string[];
335
+ /**
336
+ * Built-in Slack `eval` / `eval list` / `eval status` directive that runs
337
+ * playground eval batches (`host.evals` / `/v1/dev/evals/runs`) without an
338
+ * agent turn. Defaults to `true`. Set `false` to leave `eval …` for the
339
+ * model handler instead.
340
+ */
341
+ evalCommands?: boolean;
335
342
  /** Optional teardown (e.g. stop example-local debouncers). Runs after Socket Mode stops. */
336
343
  onStop?: () => void | Promise<void>;
337
344
  }