@cursor/july 0.1.27 → 0.1.28
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.
- package/dist/bin/agent-serve.js +12 -3
- package/dist/channels/slack/auth.d.ts +13 -13
- package/dist/channels/slack/auth.d.ts.map +1 -1
- package/dist/channels/slack/auth.js +19 -10
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +3 -1
- package/dist/channels/slack/defaults.d.ts +7 -4
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +14 -4
- package/dist/channels/slack/dispatch.d.ts +6 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +59 -21
- package/dist/channels/slack/external-policy.d.ts +27 -0
- package/dist/channels/slack/external-policy.d.ts.map +1 -0
- package/dist/channels/slack/external-policy.js +53 -0
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/interactive.d.ts +6 -0
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +42 -15
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +19 -6
- package/dist/channels/slack/types.d.ts +10 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.BICLoJYF.js → app.CqjrYaT2.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.VWQoPFJ1.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.wN4Ko9dT.js → VPLocalSearchBox.ag-MwUel.js} +1 -1
- package/dist/docs/assets/chunks/{theme.dnnTE-Co.js → theme.BS-DJWat.js} +2 -2
- package/dist/docs/assets/{guides_slack.md.D_xFKlnF.js → guides_slack.md.CjmJSvZS.js} +8 -4
- package/dist/docs/assets/guides_slack.md.CjmJSvZS.lean.js +1 -0
- package/dist/docs/assets/guides_webhooks.md.wSOYas3X.js +183 -0
- package/dist/docs/assets/{guides_webhooks.md.B1EswtUu.lean.js → guides_webhooks.md.wSOYas3X.lean.js} +1 -1
- package/dist/docs/assets/{index.md.Dfv5ic9t.js → index.md.CH_s5uZe.js} +1 -1
- package/dist/docs/assets/{reference_channels.md.DdmiKgqf.js → reference_channels.md.CDhTRfUz.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.DdmiKgqf.lean.js → reference_channels.md.CDhTRfUz.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/slack.html +11 -7
- package/dist/docs/guides/webhooks.html +138 -5
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +5 -5
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +5 -5
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/internal/cli-ax.d.ts +8 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +116 -57
- package/dist/internal/cursor-slack-relay.d.ts +2 -0
- package/dist/internal/cursor-slack-relay.d.ts.map +1 -1
- package/dist/internal/cursor-slack-relay.js +1 -0
- package/dist/internal/deploy-client.d.ts +2 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +1 -0
- package/dist/internal/discovery.js +8 -1
- package/dist/internal/server.js +10 -5
- package/dist/playground/assets/{index-BDCeW4uc.js → index-CkN74U_L.js} +48 -47
- package/dist/playground/index.html +1 -1
- package/docs/README.md +1 -0
- package/docs/guides/slack.md +32 -7
- package/docs/guides/webhooks.md +183 -0
- package/docs/reference/channels.md +2 -1
- package/package.json +1 -1
- package/skills/setup-slack/SKILL.md +3 -0
- package/src/bin/agent-serve.ts +12 -3
- package/src/channels/slack/auth.ts +29 -22
- package/src/channels/slack/cursor-account.ts +2 -0
- package/src/channels/slack/defaults.ts +24 -4
- package/src/channels/slack/dispatch.ts +42 -1
- package/src/channels/slack/external-policy.ts +62 -0
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/interactive.ts +42 -10
- package/src/channels/slack/slack-channel.ts +20 -3
- package/src/channels/slack/types.ts +12 -0
- package/src/internal/cli-ax.ts +110 -34
- package/src/internal/cursor-slack-relay.ts +3 -0
- package/src/internal/deploy-client.ts +3 -0
- package/src/internal/discovery.ts +8 -1
- package/src/internal/server.ts +9 -5
- package/dist/docs/assets/chunks/@localSearchIndexroot.CQJytDwU.js +0 -1
- package/dist/docs/assets/guides_slack.md.D_xFKlnF.lean.js +0 -1
- package/dist/docs/assets/guides_webhooks.md.B1EswtUu.js +0 -50
- /package/dist/docs/assets/{index.md.Dfv5ic9t.lean.js → index.md.CH_s5uZe.lean.js} +0 -0
|
@@ -33,6 +33,7 @@ export * from "./dispatch.js";
|
|
|
33
33
|
export * from "./doctor.js";
|
|
34
34
|
export * from "./eval-directive.js";
|
|
35
35
|
export * from "./event-id-dedupe.js";
|
|
36
|
+
export * from "./external-policy.js";
|
|
36
37
|
export * from "./inbound.js";
|
|
37
38
|
export * from "./init.js";
|
|
38
39
|
export * from "./interactive.js";
|
|
@@ -20,6 +20,7 @@ import { updateMessage } from "./api.js";
|
|
|
20
20
|
import { decisionFromActionId, parseApprovalActionValue } from "./approvals.js";
|
|
21
21
|
import { buildSlackInteractiveApprovalAuth } from "./auth.js";
|
|
22
22
|
import { dispatchInboundMessage } from "./dispatch.js";
|
|
23
|
+
import { isExternalSlackSender } from "./external-policy.js";
|
|
23
24
|
import { defaultSlackLog, type SlackLog } from "./log.js";
|
|
24
25
|
import type {
|
|
25
26
|
SlackBlockAction,
|
|
@@ -41,6 +42,8 @@ export interface SlackInteractiveDispatchDeps {
|
|
|
41
42
|
) => Promise<PendingApproval>;
|
|
42
43
|
waitUntil: (promise: Promise<unknown>) => void;
|
|
43
44
|
log?: SlackLog;
|
|
45
|
+
/** Defaults to true. */
|
|
46
|
+
blockExternals?: boolean;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
/**
|
|
@@ -116,11 +119,35 @@ export async function dispatchInteractiveFromPayload(
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
const teamId = attrs.team_id;
|
|
122
|
+
if (deps.blockExternals !== false && parsed.userId !== "") {
|
|
123
|
+
if (teamId === undefined || teamId === "") {
|
|
124
|
+
log.info("interactive approval dropped (missing team)", {
|
|
125
|
+
session: value.sessionId,
|
|
126
|
+
user: parsed.userId,
|
|
127
|
+
});
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (
|
|
131
|
+
await isExternalSlackSender({
|
|
132
|
+
botToken: deps.credentials?.botToken,
|
|
133
|
+
slackTeamId: teamId,
|
|
134
|
+
userId: parsed.userId,
|
|
135
|
+
})
|
|
136
|
+
) {
|
|
137
|
+
log.info("interactive approval dropped (external sender)", {
|
|
138
|
+
session: value.sessionId,
|
|
139
|
+
user: parsed.userId,
|
|
140
|
+
team: teamId,
|
|
141
|
+
});
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
119
146
|
const clickingAuth = buildSlackInteractiveApprovalAuth({
|
|
120
147
|
sessionAuth,
|
|
121
148
|
channelId: parsed.channelId || sessionChannel,
|
|
122
149
|
threadTs: parsed.threadTs || sessionThread,
|
|
123
|
-
|
|
150
|
+
teamId: teamId === undefined || teamId === "" ? undefined : teamId,
|
|
124
151
|
clickingUserId: parsed.userId,
|
|
125
152
|
});
|
|
126
153
|
|
|
@@ -204,12 +231,9 @@ export function parseSlackBlockAction(
|
|
|
204
231
|
: typeof user.name === "string"
|
|
205
232
|
? user.name
|
|
206
233
|
: undefined;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
: typeof user.team_id === "string"
|
|
211
|
-
? user.team_id
|
|
212
|
-
: undefined;
|
|
234
|
+
// Payload `team.id` only — never `user.team_id` (Connect home team).
|
|
235
|
+
// Membership gates must baseline the install workspace separately.
|
|
236
|
+
const teamId = typeof team.id === "string" ? team.id : undefined;
|
|
213
237
|
const label =
|
|
214
238
|
typeof action.text === "object" &&
|
|
215
239
|
action.text !== null &&
|
|
@@ -278,6 +302,10 @@ export interface SlackBlockActionDispatchDeps extends SlackContinuationProbes {
|
|
|
278
302
|
host: HostContext;
|
|
279
303
|
artifacts: ArtifactsApi;
|
|
280
304
|
log?: SlackLog;
|
|
305
|
+
/** Defaults to true. */
|
|
306
|
+
blockExternals?: boolean;
|
|
307
|
+
/** Install workspace from `auth.test`; baseline for the external gate. */
|
|
308
|
+
installTeamId?: string;
|
|
281
309
|
}
|
|
282
310
|
|
|
283
311
|
/**
|
|
@@ -285,7 +313,10 @@ export interface SlackBlockActionDispatchDeps extends SlackContinuationProbes {
|
|
|
285
313
|
* `action_ts` (unique per click, never a real thread message), so ask-dedupe
|
|
286
314
|
* claims and `<slack_thread>` exclusions stay per-click.
|
|
287
315
|
*/
|
|
288
|
-
function blockActionAsMessage(
|
|
316
|
+
function blockActionAsMessage(
|
|
317
|
+
action: SlackBlockAction,
|
|
318
|
+
installTeamId: string | undefined
|
|
319
|
+
): SlackMessage {
|
|
289
320
|
const text = `[clicked: ${action.label ?? action.actionId}]`;
|
|
290
321
|
return {
|
|
291
322
|
text,
|
|
@@ -294,7 +325,7 @@ function blockActionAsMessage(action: SlackBlockAction): SlackMessage {
|
|
|
294
325
|
ts: action.actionTs,
|
|
295
326
|
threadTs: action.threadTs,
|
|
296
327
|
channelId: action.channelId,
|
|
297
|
-
teamId:
|
|
328
|
+
teamId: installTeamId,
|
|
298
329
|
author: {
|
|
299
330
|
userId: action.userId,
|
|
300
331
|
userName: action.userName,
|
|
@@ -344,7 +375,7 @@ export async function dispatchBlockAction(
|
|
|
344
375
|
|
|
345
376
|
await dispatchInboundMessage({
|
|
346
377
|
kind: "block_action",
|
|
347
|
-
message: blockActionAsMessage(action),
|
|
378
|
+
message: blockActionAsMessage(action, deps.installTeamId),
|
|
348
379
|
handler: (ctx) => deps.handler(ctx, action),
|
|
349
380
|
send: deps.send,
|
|
350
381
|
credentials: deps.credentials,
|
|
@@ -353,6 +384,7 @@ export async function dispatchBlockAction(
|
|
|
353
384
|
log,
|
|
354
385
|
// Click text is synthetic — never treat it as a stop/eval directive.
|
|
355
386
|
evalCommandsEnabled: false,
|
|
387
|
+
blockExternals: deps.blockExternals,
|
|
356
388
|
hasContinuationSession: deps.hasContinuationSession,
|
|
357
389
|
isContinuationBusy: deps.isContinuationBusy,
|
|
358
390
|
getContinuationLastBotMessageTs: deps.getContinuationLastBotMessageTs,
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
buildDefaultEvents,
|
|
44
44
|
createDefaultOnChannelPost,
|
|
45
45
|
createDefaultOnInbound,
|
|
46
|
+
resolveSlackBlockExternals,
|
|
46
47
|
} from "./defaults.js";
|
|
47
48
|
import {
|
|
48
49
|
dispatchInboundFromEventBody,
|
|
@@ -152,11 +153,14 @@ export function slackChannel<const Name extends string = string>(
|
|
|
152
153
|
: (options.credentials ?? credentialsFromEnv(options.envPrefix));
|
|
153
154
|
const envKeys = slackEnvKeys(options.envPrefix);
|
|
154
155
|
const loadingMessages = options.loadingMessages;
|
|
155
|
-
const
|
|
156
|
+
const policy = options.policy;
|
|
157
|
+
const blockExternals = resolveSlackBlockExternals(policy);
|
|
158
|
+
const defaultInbound = createDefaultOnInbound(loadingMessages, policy);
|
|
156
159
|
const onAppMention = options.onAppMention ?? defaultInbound;
|
|
157
160
|
const onDirectMessage = options.onDirectMessage ?? defaultInbound;
|
|
158
161
|
const onChannelPost =
|
|
159
|
-
options.onChannelPost ??
|
|
162
|
+
options.onChannelPost ??
|
|
163
|
+
createDefaultOnChannelPost(loadingMessages, policy);
|
|
160
164
|
const engagement = options.engagement ?? {};
|
|
161
165
|
const mentionsEnabled = engagement.mentions !== false;
|
|
162
166
|
const directMessagesEnabled = engagement.directMessages !== false;
|
|
@@ -237,6 +241,8 @@ export function slackChannel<const Name extends string = string>(
|
|
|
237
241
|
mentionsEnabled,
|
|
238
242
|
directMessagesEnabled,
|
|
239
243
|
evalCommandsEnabled,
|
|
244
|
+
// cursorAccount: backend router already refuses externals.
|
|
245
|
+
blockExternals: cursorAccount ? false : blockExternals,
|
|
240
246
|
...(args.hasContinuationSession === undefined
|
|
241
247
|
? {}
|
|
242
248
|
: { hasContinuationSession: args.hasContinuationSession }),
|
|
@@ -329,10 +335,15 @@ export function slackChannel<const Name extends string = string>(
|
|
|
329
335
|
|
|
330
336
|
let botUserId: string | undefined;
|
|
331
337
|
let botId: string | undefined;
|
|
338
|
+
let installTeamId: string | undefined;
|
|
332
339
|
let webClient: InstanceType<
|
|
333
340
|
typeof import("@slack/web-api").WebClient
|
|
334
341
|
> | null = null;
|
|
335
|
-
if (
|
|
342
|
+
if (
|
|
343
|
+
channelPosts !== undefined ||
|
|
344
|
+
botMentions !== undefined ||
|
|
345
|
+
routeInteractive
|
|
346
|
+
) {
|
|
336
347
|
try {
|
|
337
348
|
const { WebClient } = await import("@slack/web-api");
|
|
338
349
|
webClient = new WebClient(botToken);
|
|
@@ -341,6 +352,8 @@ export function slackChannel<const Name extends string = string>(
|
|
|
341
352
|
typeof identity.user_id === "string" ? identity.user_id : undefined;
|
|
342
353
|
botId =
|
|
343
354
|
typeof identity.bot_id === "string" ? identity.bot_id : undefined;
|
|
355
|
+
installTeamId =
|
|
356
|
+
typeof identity.team_id === "string" ? identity.team_id : undefined;
|
|
344
357
|
} catch (error) {
|
|
345
358
|
getLog().error("bot identity lookup degraded", {
|
|
346
359
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -416,6 +429,7 @@ export function slackChannel<const Name extends string = string>(
|
|
|
416
429
|
artifacts: args.artifacts,
|
|
417
430
|
log: getLog(),
|
|
418
431
|
evalCommandsEnabled,
|
|
432
|
+
blockExternals,
|
|
419
433
|
...(args.hasContinuationSession === undefined
|
|
420
434
|
? {}
|
|
421
435
|
: {
|
|
@@ -491,6 +505,7 @@ export function slackChannel<const Name extends string = string>(
|
|
|
491
505
|
resolveApproval: args.resolveApproval,
|
|
492
506
|
waitUntil: args.waitUntil,
|
|
493
507
|
log: getLog(),
|
|
508
|
+
blockExternals,
|
|
494
509
|
}
|
|
495
510
|
);
|
|
496
511
|
if (handledAsApproval || onBlockAction === undefined) {
|
|
@@ -503,6 +518,8 @@ export function slackChannel<const Name extends string = string>(
|
|
|
503
518
|
host: args.host,
|
|
504
519
|
artifacts: args.artifacts,
|
|
505
520
|
log: getLog(),
|
|
521
|
+
blockExternals,
|
|
522
|
+
installTeamId,
|
|
506
523
|
hasContinuationSession: args.hasContinuationSession,
|
|
507
524
|
isContinuationBusy: args.isContinuationBusy,
|
|
508
525
|
getContinuationLastBotMessageTs:
|
|
@@ -432,8 +432,20 @@ export function assertSlackCursorAgentName(name: string): void {
|
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
/** `"thread"` = anyone in the thread (default); `"author"` = session starter only. */
|
|
436
|
+
export type SlackRespondTo = "thread" | "author";
|
|
437
|
+
|
|
438
|
+
export interface SlackAccessPolicy {
|
|
439
|
+
/** Defaults to `"thread"`. Applied by the default inbound handlers. */
|
|
440
|
+
respondTo?: SlackRespondTo;
|
|
441
|
+
/** Refuse Slack Connect / guest / other-workspace senders. Defaults to `true`. */
|
|
442
|
+
blockExternals?: boolean;
|
|
443
|
+
}
|
|
444
|
+
|
|
435
445
|
export interface SlackChannelOptions {
|
|
436
446
|
credentials?: SlackCredentials;
|
|
447
|
+
/** Defaults to `{ respondTo: "thread", blockExternals: true }`. */
|
|
448
|
+
policy?: SlackAccessPolicy;
|
|
437
449
|
/**
|
|
438
450
|
* Per-agent env prefix for tokens. `envPrefix: "MY_AGENT"` reads
|
|
439
451
|
* `MY_AGENT_SLACK_BOT_TOKEN` + `MY_AGENT_SLACK_APP_TOKEN`. Prefer this over
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
|
-
import { join, resolve } from "node:path";
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
13
|
import { Artifacts, JUnit } from "../evals/reporters.js";
|
|
14
14
|
import type {
|
|
15
15
|
EvalAssertionResult,
|
|
@@ -122,6 +122,12 @@ export interface AxCliOptions {
|
|
|
122
122
|
skipReport?: boolean;
|
|
123
123
|
/** `eval`: override `evals.config.ts` `maxConcurrency`. */
|
|
124
124
|
maxConcurrency?: number;
|
|
125
|
+
/**
|
|
126
|
+
* `eval` / `eval status <runId>`: also write the full results JSON to this
|
|
127
|
+
* path. `session`: write the raw NDJSON trace (same format as `run
|
|
128
|
+
* --events` and the playground download) to this path instead of printing.
|
|
129
|
+
*/
|
|
130
|
+
out?: string;
|
|
125
131
|
/** Scaffold target directory for `init`. */
|
|
126
132
|
initTarget?: string;
|
|
127
133
|
/** Tool name for `call`. */
|
|
@@ -794,6 +800,18 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
|
|
|
794
800
|
return 2;
|
|
795
801
|
}
|
|
796
802
|
}
|
|
803
|
+
if (trimNonEmpty(options.out) !== undefined) {
|
|
804
|
+
if (options.list === true) {
|
|
805
|
+
process.stderr.write("--out and --list are mutually exclusive\n");
|
|
806
|
+
return 2;
|
|
807
|
+
}
|
|
808
|
+
if (options.noWait === true) {
|
|
809
|
+
process.stderr.write(
|
|
810
|
+
`--out needs the finished results; drop --no-wait, or poll later with ${CLI_COMMAND_NAME} eval status <evalId> --out <path>\n`
|
|
811
|
+
);
|
|
812
|
+
return 2;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
797
815
|
|
|
798
816
|
const filterIds =
|
|
799
817
|
options.evalIds === undefined || options.evalIds.length === 0
|
|
@@ -888,7 +906,12 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
|
|
|
888
906
|
process.stderr.write("No matching evals found.\n");
|
|
889
907
|
return 2;
|
|
890
908
|
}
|
|
891
|
-
return printEvalResults(
|
|
909
|
+
return await printEvalResults(
|
|
910
|
+
results,
|
|
911
|
+
options,
|
|
912
|
+
undefined,
|
|
913
|
+
artifactsDir
|
|
914
|
+
);
|
|
892
915
|
} finally {
|
|
893
916
|
progress?.close();
|
|
894
917
|
}
|
|
@@ -944,7 +967,7 @@ export async function cmdEval(options: AxCliOptions): Promise<number> {
|
|
|
944
967
|
process.stderr.write("No matching evals found.\n");
|
|
945
968
|
return 2;
|
|
946
969
|
}
|
|
947
|
-
return printEvalResults(results, options, {
|
|
970
|
+
return await printEvalResults(results, options, {
|
|
948
971
|
runId: snapshot.runId,
|
|
949
972
|
status: snapshot.status,
|
|
950
973
|
error: snapshot.error,
|
|
@@ -1049,8 +1072,8 @@ async function printEvalAccepted(
|
|
|
1049
1072
|
agentUrl
|
|
1050
1073
|
);
|
|
1051
1074
|
if (options.json) {
|
|
1052
|
-
|
|
1053
|
-
JSON.stringify(
|
|
1075
|
+
process.stdout.write(
|
|
1076
|
+
`${JSON.stringify(
|
|
1054
1077
|
{
|
|
1055
1078
|
ok: true,
|
|
1056
1079
|
accepted: true,
|
|
@@ -1064,7 +1087,7 @@ async function printEvalAccepted(
|
|
|
1064
1087
|
},
|
|
1065
1088
|
null,
|
|
1066
1089
|
2
|
|
1067
|
-
)
|
|
1090
|
+
)}\n`
|
|
1068
1091
|
);
|
|
1069
1092
|
} else {
|
|
1070
1093
|
const durable = started.config.durableRuns === true ? " (persisted)" : "";
|
|
@@ -1168,7 +1191,7 @@ export async function cmdEvalCancel(
|
|
|
1168
1191
|
fetchImpl: options.fetchImpl,
|
|
1169
1192
|
});
|
|
1170
1193
|
if (options.json) {
|
|
1171
|
-
|
|
1194
|
+
process.stdout.write(`${JSON.stringify(snap, null, 2)}\n`);
|
|
1172
1195
|
} else {
|
|
1173
1196
|
process.stdout.write(
|
|
1174
1197
|
`Cancelled Eval ID ${snap.runId} (${snap.summary.done}/${snap.summary.total} cases finalized)\n`
|
|
@@ -1214,6 +1237,13 @@ export async function cmdEvalStatus(
|
|
|
1214
1237
|
process.stderr.write("eval status does not accept --list or --no-wait\n");
|
|
1215
1238
|
return 2;
|
|
1216
1239
|
}
|
|
1240
|
+
if (
|
|
1241
|
+
trimNonEmpty(options.out) !== undefined &&
|
|
1242
|
+
(runId === undefined || runId === "")
|
|
1243
|
+
) {
|
|
1244
|
+
process.stderr.write("eval status --out requires an eval id\n");
|
|
1245
|
+
return 2;
|
|
1246
|
+
}
|
|
1217
1247
|
|
|
1218
1248
|
const target = await resolveEvalTarget(options);
|
|
1219
1249
|
if (typeof target === "number") {
|
|
@@ -1232,7 +1262,9 @@ export async function cmdEvalStatus(
|
|
|
1232
1262
|
fetchImpl: options.fetchImpl,
|
|
1233
1263
|
});
|
|
1234
1264
|
if (options.json) {
|
|
1235
|
-
|
|
1265
|
+
process.stdout.write(
|
|
1266
|
+
`${JSON.stringify({ runs, activeRunId }, null, 2)}\n`
|
|
1267
|
+
);
|
|
1236
1268
|
} else if (runs.length === 0) {
|
|
1237
1269
|
process.stdout.write("No eval runs on this agent.\n");
|
|
1238
1270
|
} else {
|
|
@@ -1252,8 +1284,12 @@ export async function cmdEvalStatus(
|
|
|
1252
1284
|
auth,
|
|
1253
1285
|
fetchImpl: options.fetchImpl,
|
|
1254
1286
|
});
|
|
1287
|
+
const outPath = await writeJsonOutFile(options.out, snap);
|
|
1255
1288
|
if (options.json) {
|
|
1256
|
-
|
|
1289
|
+
process.stdout.write(`${JSON.stringify(snap, null, 2)}\n`);
|
|
1290
|
+
if (outPath !== undefined) {
|
|
1291
|
+
process.stderr.write(`results: ${outPath}\n`);
|
|
1292
|
+
}
|
|
1257
1293
|
} else {
|
|
1258
1294
|
process.stdout.write(`${formatEvalRunSummaryLine(snap)}\n`);
|
|
1259
1295
|
if (snap.error !== undefined && snap.error !== "") {
|
|
@@ -1271,6 +1307,9 @@ export async function cmdEvalStatus(
|
|
|
1271
1307
|
process.stdout.write(` error: ${c.error}\n`);
|
|
1272
1308
|
}
|
|
1273
1309
|
}
|
|
1310
|
+
if (outPath !== undefined) {
|
|
1311
|
+
process.stdout.write(`results: ${outPath}\n`);
|
|
1312
|
+
}
|
|
1274
1313
|
}
|
|
1275
1314
|
if (snap.status === "running") {
|
|
1276
1315
|
return 3;
|
|
@@ -1333,7 +1372,7 @@ function printListedEvals(
|
|
|
1333
1372
|
emptyMessage: string
|
|
1334
1373
|
): number {
|
|
1335
1374
|
if (options.json) {
|
|
1336
|
-
|
|
1375
|
+
process.stdout.write(`${JSON.stringify(listed, null, 2)}\n`);
|
|
1337
1376
|
} else if (listed.length === 0) {
|
|
1338
1377
|
process.stdout.write(`${emptyMessage}\n`);
|
|
1339
1378
|
} else {
|
|
@@ -1438,7 +1477,7 @@ const EVAL_VERDICT_LABEL: Record<EvalVerdict, string> = {
|
|
|
1438
1477
|
skipped: "SKIP",
|
|
1439
1478
|
};
|
|
1440
1479
|
|
|
1441
|
-
function printEvalResults(
|
|
1480
|
+
async function printEvalResults(
|
|
1442
1481
|
results: EvalRunResult[],
|
|
1443
1482
|
options: AxCliOptions,
|
|
1444
1483
|
batch?: {
|
|
@@ -1447,7 +1486,7 @@ function printEvalResults(
|
|
|
1447
1486
|
error?: string;
|
|
1448
1487
|
},
|
|
1449
1488
|
artifactsDir?: string
|
|
1450
|
-
): number {
|
|
1489
|
+
): Promise<number> {
|
|
1451
1490
|
const strict = options.strict === true;
|
|
1452
1491
|
const counts = { passed: 0, failed: 0, scored: 0, skipped: 0 };
|
|
1453
1492
|
for (const result of results) {
|
|
@@ -1462,28 +1501,29 @@ function printEvalResults(
|
|
|
1462
1501
|
process.stderr.write(`eval run error: ${batch.error}\n`);
|
|
1463
1502
|
}
|
|
1464
1503
|
|
|
1504
|
+
const payload = {
|
|
1505
|
+
ok,
|
|
1506
|
+
passed: counts.passed,
|
|
1507
|
+
failed: counts.failed,
|
|
1508
|
+
scored: counts.scored,
|
|
1509
|
+
skipped: counts.skipped,
|
|
1510
|
+
strict,
|
|
1511
|
+
results,
|
|
1512
|
+
...(artifactsDir === undefined ? {} : { artifactsDir }),
|
|
1513
|
+
...(batch?.runId === undefined ? {} : { runId: batch.runId }),
|
|
1514
|
+
...(batch?.status === undefined ? {} : { status: batch.status }),
|
|
1515
|
+
...(batch?.error === undefined || batch.error === ""
|
|
1516
|
+
? {}
|
|
1517
|
+
: { error: batch.error }),
|
|
1518
|
+
};
|
|
1519
|
+
const outPath = await writeJsonOutFile(options.out, payload);
|
|
1520
|
+
|
|
1465
1521
|
if (options.json) {
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
failed: counts.failed,
|
|
1472
|
-
scored: counts.scored,
|
|
1473
|
-
skipped: counts.skipped,
|
|
1474
|
-
strict,
|
|
1475
|
-
results,
|
|
1476
|
-
...(artifactsDir === undefined ? {} : { artifactsDir }),
|
|
1477
|
-
...(batch?.runId === undefined ? {} : { runId: batch.runId }),
|
|
1478
|
-
...(batch?.status === undefined ? {} : { status: batch.status }),
|
|
1479
|
-
...(batch?.error === undefined || batch.error === ""
|
|
1480
|
-
? {}
|
|
1481
|
-
: { error: batch.error }),
|
|
1482
|
-
},
|
|
1483
|
-
null,
|
|
1484
|
-
2
|
|
1485
|
-
)
|
|
1486
|
-
);
|
|
1522
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
1523
|
+
if (outPath !== undefined) {
|
|
1524
|
+
// stdout stays pure JSON; report the file on stderr.
|
|
1525
|
+
process.stderr.write(`results: ${outPath}\n`);
|
|
1526
|
+
}
|
|
1487
1527
|
} else {
|
|
1488
1528
|
for (const r of results) {
|
|
1489
1529
|
const verdict = evalVerdictOf(r);
|
|
@@ -1532,10 +1572,31 @@ function printEvalResults(
|
|
|
1532
1572
|
if (artifactsDir !== undefined) {
|
|
1533
1573
|
process.stdout.write(`artifacts: ${artifactsDir}\n`);
|
|
1534
1574
|
}
|
|
1575
|
+
if (outPath !== undefined) {
|
|
1576
|
+
process.stdout.write(`results: ${outPath}\n`);
|
|
1577
|
+
}
|
|
1535
1578
|
}
|
|
1536
1579
|
return ok ? 0 : 1;
|
|
1537
1580
|
}
|
|
1538
1581
|
|
|
1582
|
+
/**
|
|
1583
|
+
* `--out <path>`: write a JSON payload (results / run snapshot), creating
|
|
1584
|
+
* parent directories. Returns the resolved path, or undefined when unset.
|
|
1585
|
+
*/
|
|
1586
|
+
async function writeJsonOutFile(
|
|
1587
|
+
out: string | undefined,
|
|
1588
|
+
payload: unknown
|
|
1589
|
+
): Promise<string | undefined> {
|
|
1590
|
+
const trimmed = trimNonEmpty(out);
|
|
1591
|
+
if (trimmed === undefined) {
|
|
1592
|
+
return undefined;
|
|
1593
|
+
}
|
|
1594
|
+
const target = resolve(trimmed);
|
|
1595
|
+
await mkdir(dirname(target), { recursive: true });
|
|
1596
|
+
await writeFile(target, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
1597
|
+
return target;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1539
1600
|
/**
|
|
1540
1601
|
* One assertion line. Soft assertions read `~` rather than `✗` when they miss,
|
|
1541
1602
|
* so a tracked score is visibly different from a failed gate.
|
|
@@ -1721,6 +1782,8 @@ export async function cmdSessions(options: AxCliOptions): Promise<number> {
|
|
|
1721
1782
|
/**
|
|
1722
1783
|
* `session <id>` — view a full session (trajectory text by default).
|
|
1723
1784
|
* `--json` → trajectory JSON; `--events` (`rawEvents`) → raw event list.
|
|
1785
|
+
* `--out <path>` → write the raw NDJSON trace to a file instead of printing
|
|
1786
|
+
* (same format as `run --events` and the playground download).
|
|
1724
1787
|
* Interactive reattach: `resume [id]` / `chat --resume` (or `chat --session`).
|
|
1725
1788
|
*/
|
|
1726
1789
|
export async function cmdSession(
|
|
@@ -1729,7 +1792,7 @@ export async function cmdSession(
|
|
|
1729
1792
|
): Promise<number> {
|
|
1730
1793
|
if (sessionId === undefined || sessionId.trim() === "") {
|
|
1731
1794
|
process.stderr.write(
|
|
1732
|
-
`Usage: ${CLI_COMMAND_NAME} session <sessionId> [--url | --prod] [--json | --text | --events]\n`
|
|
1795
|
+
`Usage: ${CLI_COMMAND_NAME} session <sessionId> [--url | --prod] [--json | --text | --events] [--out <file.ndjson>]\n`
|
|
1733
1796
|
);
|
|
1734
1797
|
return 2;
|
|
1735
1798
|
}
|
|
@@ -1754,6 +1817,19 @@ export async function cmdSession(
|
|
|
1754
1817
|
fetchImpl: options.fetchImpl,
|
|
1755
1818
|
});
|
|
1756
1819
|
|
|
1820
|
+
const outPath = trimNonEmpty(options.out);
|
|
1821
|
+
if (outPath !== undefined) {
|
|
1822
|
+
const targetPath = resolve(outPath);
|
|
1823
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
1824
|
+
await writeFile(
|
|
1825
|
+
targetPath,
|
|
1826
|
+
`${events.map((e) => JSON.stringify(e)).join("\n")}\n`,
|
|
1827
|
+
"utf8"
|
|
1828
|
+
);
|
|
1829
|
+
process.stdout.write(`trace: ${targetPath} (${events.length} events)\n`);
|
|
1830
|
+
return 0;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1757
1833
|
if (options.rawEvents === true) {
|
|
1758
1834
|
process.stdout.write(
|
|
1759
1835
|
`${JSON.stringify({ sessionId, events }, null, 2)}\n`
|
|
@@ -23,6 +23,8 @@ export interface CursorSlackAgentRegistration {
|
|
|
23
23
|
name: string;
|
|
24
24
|
iconUrl?: string;
|
|
25
25
|
iconEmoji?: string;
|
|
26
|
+
/** Opt out of the default external-sender block. */
|
|
27
|
+
blockExternals?: boolean;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
interface RegistrationResponse {
|
|
@@ -160,6 +162,7 @@ export class CursorSlackRelay {
|
|
|
160
162
|
name: agent.name,
|
|
161
163
|
iconUrl: agent.iconUrl,
|
|
162
164
|
iconEmoji: agent.iconEmoji,
|
|
165
|
+
blockExternals: agent.blockExternals === false ? false : undefined,
|
|
163
166
|
})),
|
|
164
167
|
};
|
|
165
168
|
},
|
|
@@ -148,6 +148,8 @@ export interface Deployment {
|
|
|
148
148
|
engineGeneration?: number | null;
|
|
149
149
|
publicId?: string;
|
|
150
150
|
engineUrl?: string | null;
|
|
151
|
+
/** Anyrun pod id for the active engine, when coordinates are known. */
|
|
152
|
+
enginePodId?: string | null;
|
|
151
153
|
lastError?: string | null;
|
|
152
154
|
engineAlias: string;
|
|
153
155
|
desiredUpdatedAt?: string;
|
|
@@ -259,6 +261,7 @@ const deploymentSchema = z.object({
|
|
|
259
261
|
engineGeneration: z.number().nullable().optional(),
|
|
260
262
|
publicId: z.string().optional(),
|
|
261
263
|
engineUrl: z.string().nullable().optional(),
|
|
264
|
+
enginePodId: z.string().nullable().optional(),
|
|
262
265
|
lastError: z.string().nullable().optional(),
|
|
263
266
|
engineAlias: z.string(),
|
|
264
267
|
desiredUpdatedAt: z.string().optional(),
|
|
@@ -2000,7 +2000,14 @@ function isModuleFile(fileName: string): boolean {
|
|
|
2000
2000
|
if (fileName.endsWith(".d.ts") || fileName.startsWith(".")) {
|
|
2001
2001
|
return false;
|
|
2002
2002
|
}
|
|
2003
|
-
|
|
2003
|
+
if (!MODULE_EXTENSIONS.has(extname(fileName))) {
|
|
2004
|
+
return false;
|
|
2005
|
+
}
|
|
2006
|
+
// Colocated unit tests (Vitest/Jest convention) are not definitions.
|
|
2007
|
+
// Without this, `channels/slack.test.ts` is scanned as a channel named
|
|
2008
|
+
// "slack.test", fails NAME_PATTERN, and the whole agent refuses to serve.
|
|
2009
|
+
const stem = moduleStem(fileName);
|
|
2010
|
+
return !stem.endsWith(".test") && !stem.endsWith(".spec");
|
|
2004
2011
|
}
|
|
2005
2012
|
|
|
2006
2013
|
function moduleStem(fileName: string): string {
|
package/src/internal/server.ts
CHANGED
|
@@ -1747,11 +1747,15 @@ async function startCursorSlackRelay(input: {
|
|
|
1747
1747
|
|
|
1748
1748
|
// Optional fields may be undefined — the relay JSON-serializes the
|
|
1749
1749
|
// registration body, which drops them.
|
|
1750
|
-
const agents: CursorSlackAgentRegistration[] = targets.map((target) =>
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1750
|
+
const agents: CursorSlackAgentRegistration[] = targets.map((target) => {
|
|
1751
|
+
const policy = target.accountRuntime.options.policy;
|
|
1752
|
+
return {
|
|
1753
|
+
name: target.agentName,
|
|
1754
|
+
iconUrl: target.accountRuntime.options.agentIcon?.url,
|
|
1755
|
+
iconEmoji: target.accountRuntime.options.agentIcon?.emoji,
|
|
1756
|
+
blockExternals: policy?.blockExternals === false ? false : undefined,
|
|
1757
|
+
};
|
|
1758
|
+
});
|
|
1755
1759
|
|
|
1756
1760
|
const targetByName = new Map(
|
|
1757
1761
|
targets.map((target) => [target.agentName.toLowerCase(), target])
|