@cursor/july 0.1.94 → 0.1.95
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/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +14 -2
- package/dist/channels/github/types.d.ts +18 -3
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +11 -3
- package/dist/channels/origin/origin-webhook.d.ts +11 -1
- package/dist/channels/origin/origin-webhook.d.ts.map +1 -1
- package/dist/channels/origin/origin-webhook.js +23 -3
- package/dist/channels/origin/types.d.ts +8 -0
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/pr-ref.d.ts +7 -1
- package/dist/channels/slack/pr-ref.d.ts.map +1 -1
- package/dist/channels/slack/pr-ref.js +42 -23
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.CFDEas4I.js → app.BBj0klBO.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.oqPawjiD.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.B1IIYpYS.js → VPLocalSearchBox.CUEXpL78.js} +1 -1
- package/dist/docs/assets/chunks/{theme.Ct4NSiLm.js → theme.DabmQnia.js} +2 -2
- package/dist/docs/assets/index.md.BoHaMdeZ.js +5 -0
- package/dist/docs/assets/{index.md.DRakGHFe.lean.js → index.md.BoHaMdeZ.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.js → reference_cli.md.BsMOxDvh.js} +3 -2
- package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.lean.js → reference_cli.md.BsMOxDvh.lean.js} +1 -1
- package/dist/docs/assets/{reference_skills.md.8son6Hjm.js → reference_skills.md.BFW9retM.js} +3 -3
- package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.js → templates_agentic-owners.md.BZSH4N9z.js} +1 -1
- package/dist/docs/assets/{templates_demo.md.DhFcWN6j.js → templates_demo.md.BeQX9V3H.js} +1 -1
- package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.js → templates_pr-autofixer.md.x5zl6-GT.js} +2 -2
- package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.lean.js → templates_pr-autofixer.md.x5zl6-GT.lean.js} +1 -1
- package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.js +4 -0
- package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.lean.js +1 -0
- package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.js → templates_security-reviewer.md.lshxbCLK.js} +2 -2
- package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.lean.js → templates_security-reviewer.md.lshxbCLK.lean.js} +1 -1
- package/dist/docs/assets/{templates_triage.md.CVlpctKS.js → templates_triage.md.Co4UNzkZ.js} +3 -3
- package/dist/docs/assets/{templates_triage.md.CVlpctKS.lean.js → templates_triage.md.Co4UNzkZ.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +4 -4
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +3 -1
- package/dist/docs/llms-full.txt +90 -26
- package/dist/docs/llms.txt +1 -0
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +7 -6
- package/dist/docs/reference/cli.md +3 -1
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +7 -7
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +7 -7
- package/dist/docs/templates/agentic-owners.md +1 -1
- package/dist/docs/templates/demo.html +6 -6
- package/dist/docs/templates/demo.md +3 -2
- package/dist/docs/templates/pr-autofixer.html +6 -6
- package/dist/docs/templates/pr-autofixer.md +7 -13
- package/dist/docs/templates/security-help.html +30 -0
- package/dist/docs/templates/security-help.md +65 -0
- package/dist/docs/templates/security-reviewer.html +6 -6
- package/dist/docs/templates/security-reviewer.md +1 -2
- package/dist/docs/templates/triage.html +7 -7
- package/dist/docs/templates/triage.md +2 -6
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/files-backends/cursor-hosted.d.ts +6 -2
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +2 -2
- package/dist/files.d.ts +2 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +5 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +135 -6
- package/dist/internal/conversation-mirror.d.ts +82 -0
- package/dist/internal/conversation-mirror.d.ts.map +1 -0
- package/dist/internal/conversation-mirror.js +251 -0
- package/dist/internal/deferred-channel-session.d.ts +20 -0
- package/dist/internal/deferred-channel-session.d.ts.map +1 -0
- package/dist/internal/deferred-channel-session.js +62 -0
- package/dist/internal/deploy-client.d.ts +13 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +11 -1
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +11 -5
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +22 -2
- package/dist/internal/framework-storage-selection.d.ts +32 -16
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +51 -17
- package/dist/internal/hosted-admission-context.d.ts +20 -0
- package/dist/internal/hosted-admission-context.d.ts.map +1 -0
- package/dist/internal/hosted-admission-context.js +31 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +5 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +33 -1
- package/dist/internal/hosted-delivery.d.ts +4 -2
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +75 -35
- package/dist/internal/hosted-managed-automation.d.ts +30 -0
- package/dist/internal/hosted-managed-automation.d.ts.map +1 -0
- package/dist/internal/hosted-managed-automation.js +58 -0
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +10 -98
- package/dist/internal/scm/pr-url.d.ts +14 -0
- package/dist/internal/scm/pr-url.d.ts.map +1 -0
- package/dist/internal/scm/pr-url.js +65 -0
- package/dist/internal/sdk-runner.d.ts +14 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +41 -2
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +10 -2
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +9 -0
- package/dist/playground/assets/index-CF7hUDnQ.css +1 -0
- package/dist/playground/assets/{index-B3JCyigB.js → index-CZA1uEWA.js} +48 -48
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +7 -2
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +2 -2
- package/docs/README.md +3 -1
- package/docs/reference/cli.md +3 -1
- package/docs/templates/agentic-owners.md +1 -1
- package/docs/templates/demo.md +3 -2
- package/docs/templates/pr-autofixer.md +7 -13
- package/docs/templates/security-help.md +70 -0
- package/docs/templates/security-reviewer.md +1 -2
- package/docs/templates/triage.md +2 -6
- package/package.json +1 -1
- package/src/channels/github/github-channel.ts +23 -8
- package/src/channels/github/types.ts +19 -2
- package/src/channels/origin/origin-channel.ts +13 -1
- package/src/channels/origin/origin-webhook.ts +27 -3
- package/src/channels/origin/types.ts +8 -0
- package/src/channels/slack/pr-ref.ts +56 -25
- package/src/files-backends/cursor-hosted.ts +9 -3
- package/src/files.ts +11 -0
- package/src/internal/cli-deploy.ts +171 -7
- package/src/internal/conversation-mirror.ts +330 -0
- package/src/internal/deferred-channel-session.ts +61 -0
- package/src/internal/deploy-client.ts +24 -1
- package/src/internal/deploy-manifest.ts +10 -5
- package/src/internal/discovery.ts +25 -2
- package/src/internal/framework-storage-selection.ts +61 -19
- package/src/internal/hosted-admission-context.ts +37 -0
- package/src/internal/hosted-delivery-protocol.ts +44 -1
- package/src/internal/hosted-delivery.ts +95 -40
- package/src/internal/hosted-managed-automation.ts +72 -0
- package/src/internal/review-comments.ts +10 -113
- package/src/internal/scm/pr-url.ts +95 -0
- package/src/internal/sdk-runner.ts +57 -2
- package/src/internal/server.ts +10 -0
- package/src/internal/session-engine.ts +12 -0
- package/src/storage-backends/cursor-hosted.ts +10 -3
- package/templates/agentic-owners/README.md +1 -1
- package/templates/agentic-owners/agent/agent.ts +0 -10
- package/templates/agentic-owners/agent/channels/github.ts +5 -14
- package/templates/agentic-owners/agent/lib/config.ts +0 -8
- package/templates/agentic-owners/agent/lib/review.ts +2 -15
- package/templates/agentic-owners/agent/tools/record_review.ts +2 -4
- package/templates/demo/agent/agent.ts +0 -10
- package/templates/pr-autofixer/README.md +0 -2
- package/templates/pr-autofixer/agent/agent.ts +0 -11
- package/templates/pr-autofixer/agent/channels/slack.ts +0 -7
- package/templates/pr-autofixer/agent/lib/pr-state.ts +5 -17
- package/templates/pr-autofixer/agent/lib/repos.ts +0 -1
- package/templates/security-help/README.md +2 -2
- package/templates/security-help/agent/agent.ts +1 -2
- package/templates/security-help/agent/channels/slack.ts +0 -3
- package/templates/security-help/agent/instructions.md +9 -10
- package/templates/security-help/agent/skills/access-request.md +1 -1
- package/templates/security-help/agent/skills/faq.md +31 -0
- package/templates/security-help/agent/skills/security-playbooks.md +1 -1
- package/templates/security-help/package.json +1 -2
- package/templates/security-reviewer/agent/agent.ts +0 -10
- package/templates/triage/agent/agent.ts +0 -10
- package/templates/triage/agent/channels/queue.ts +1 -1
- package/templates/triage/agent/channels/webhook.ts +1 -3
- package/dist/docs/assets/chunks/@localSearchIndexroot.DU3U2Ij2.js +0 -1
- package/dist/docs/assets/index.md.DRakGHFe.js +0 -5
- package/dist/playground/assets/index-D9N7-q97.css +0 -1
- package/templates/security-help/agent/knowledge/faq/approvals.md +0 -5
- package/templates/security-help/agent/knowledge/faq/channels.md +0 -6
- package/templates/security-help/agent/knowledge/faq/phishing.md +0 -10
- package/templates/security-help/agent/skills/security-first-pass.md +0 -15
- /package/dist/docs/assets/{reference_skills.md.8son6Hjm.lean.js → reference_skills.md.BFW9retM.lean.js} +0 -0
- /package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.lean.js → templates_agentic-owners.md.BZSH4N9z.lean.js} +0 -0
- /package/dist/docs/assets/{templates_demo.md.DhFcWN6j.lean.js → templates_demo.md.BeQX9V3H.lean.js} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import type { HostGitHubClient } from "../types.js";
|
|
3
|
+
import { parsePrUrl } from "./scm/pr-url.js";
|
|
3
4
|
|
|
4
5
|
export type ReviewCommentPublisher = "local_gh" | "host_github" | "host_origin";
|
|
5
6
|
|
|
@@ -198,8 +199,15 @@ export type ReviewThreadResolverImplementation = {
|
|
|
198
199
|
export function resolveScmReviewTarget(
|
|
199
200
|
prUrl: string
|
|
200
201
|
): ScmReviewTarget | undefined {
|
|
201
|
-
const parsed =
|
|
202
|
-
|
|
202
|
+
const parsed = parsePrUrl(prUrl);
|
|
203
|
+
if (parsed === undefined) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
provider: parsed.provider,
|
|
208
|
+
repo: `${parsed.owner}/${parsed.repo}`,
|
|
209
|
+
prNumber: parsed.number,
|
|
210
|
+
};
|
|
203
211
|
}
|
|
204
212
|
|
|
205
213
|
/**
|
|
@@ -1243,117 +1251,6 @@ function skippedResolve(detail: string): ResolveReviewThreadsResult {
|
|
|
1243
1251
|
};
|
|
1244
1252
|
}
|
|
1245
1253
|
|
|
1246
|
-
const ORIGIN_PR_PAGE_HOST_PATTERN = /^origin(-[a-z0-9]+)?\.cursor\.com$/i;
|
|
1247
|
-
|
|
1248
|
-
/**
|
|
1249
|
-
* Provider-neutral PR URL parse. GitHub.com plus the Origin page shapes
|
|
1250
|
-
* `parsePrTarget` in security-review-lib accepts. Duplicated here so
|
|
1251
|
-
* agent-serve does not import that package.
|
|
1252
|
-
*/
|
|
1253
|
-
function parseReviewPrUrl(
|
|
1254
|
-
prUrl: string
|
|
1255
|
-
):
|
|
1256
|
-
| { kind: "valid"; value: ScmReviewTarget }
|
|
1257
|
-
| { kind: "invalid"; detail: string } {
|
|
1258
|
-
let url: URL;
|
|
1259
|
-
try {
|
|
1260
|
-
url = new URL(prUrl);
|
|
1261
|
-
} catch (error) {
|
|
1262
|
-
return {
|
|
1263
|
-
kind: "invalid",
|
|
1264
|
-
detail: `unparseable PR URL: ${error instanceof Error ? error.message : String(error)}`,
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
const parts = url.pathname.split("/").filter(Boolean);
|
|
1269
|
-
const hostname = url.hostname;
|
|
1270
|
-
|
|
1271
|
-
if (hostname === "github.com" && parts.length >= 4 && parts[2] === "pull") {
|
|
1272
|
-
const prNumber = Number(parts[3]);
|
|
1273
|
-
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
1274
|
-
return {
|
|
1275
|
-
kind: "valid",
|
|
1276
|
-
value: {
|
|
1277
|
-
provider: "github",
|
|
1278
|
-
repo: `${parts[0]}/${parts[1]}`,
|
|
1279
|
-
prNumber,
|
|
1280
|
-
},
|
|
1281
|
-
};
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
if (
|
|
1286
|
-
ORIGIN_PR_PAGE_HOST_PATTERN.test(hostname) &&
|
|
1287
|
-
parts.length >= 4 &&
|
|
1288
|
-
parts[2] === "github"
|
|
1289
|
-
) {
|
|
1290
|
-
const prNumber = Number(parts[3]);
|
|
1291
|
-
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
1292
|
-
return {
|
|
1293
|
-
kind: "valid",
|
|
1294
|
-
value: {
|
|
1295
|
-
provider: "origin",
|
|
1296
|
-
repo: `${parts[0]}/${parts[1]}`,
|
|
1297
|
-
prNumber,
|
|
1298
|
-
},
|
|
1299
|
-
};
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
if (
|
|
1304
|
-
/^(www\.)?cursor\.com$/i.test(hostname) &&
|
|
1305
|
-
parts[0] === "codebase" &&
|
|
1306
|
-
parts.length >= 5 &&
|
|
1307
|
-
parts[3] === "pull"
|
|
1308
|
-
) {
|
|
1309
|
-
const prNumber = Number(parts[4]);
|
|
1310
|
-
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
1311
|
-
return {
|
|
1312
|
-
kind: "valid",
|
|
1313
|
-
value: {
|
|
1314
|
-
provider: "origin",
|
|
1315
|
-
repo: `${parts[1]}/${parts[2]}`,
|
|
1316
|
-
prNumber,
|
|
1317
|
-
},
|
|
1318
|
-
};
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
if (hostname === "review.cursor.com" && parts[0] === "pr") {
|
|
1323
|
-
if (parts.length >= 5 && parts[3] === "github") {
|
|
1324
|
-
const prNumber = Number(parts[4]);
|
|
1325
|
-
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
1326
|
-
return {
|
|
1327
|
-
kind: "valid",
|
|
1328
|
-
value: {
|
|
1329
|
-
provider: "origin",
|
|
1330
|
-
repo: `${parts[1]}/${parts[2]}`,
|
|
1331
|
-
prNumber,
|
|
1332
|
-
},
|
|
1333
|
-
};
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
if (parts.length >= 4) {
|
|
1337
|
-
const prNumber = Number(parts[3]);
|
|
1338
|
-
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
1339
|
-
return {
|
|
1340
|
-
kind: "valid",
|
|
1341
|
-
value: {
|
|
1342
|
-
provider: "origin",
|
|
1343
|
-
repo: `${parts[1]}/${parts[2]}`,
|
|
1344
|
-
prNumber,
|
|
1345
|
-
},
|
|
1346
|
-
};
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
return {
|
|
1352
|
-
kind: "invalid",
|
|
1353
|
-
detail: "unparseable PR URL: not a GitHub or Origin pull request",
|
|
1354
|
-
};
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
1254
|
function parseGithubPrUrl(
|
|
1358
1255
|
prUrl: string
|
|
1359
1256
|
):
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral PR URL parse. GitHub.com plus the Origin page shapes
|
|
3
|
+
* humans paste: cursor.com/codebase, review.cursor.com/pr, origin.cursor.com.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type PrProvider = "github" | "origin";
|
|
7
|
+
|
|
8
|
+
export type ParsedPrUrl = {
|
|
9
|
+
provider: PrProvider;
|
|
10
|
+
owner: string;
|
|
11
|
+
repo: string;
|
|
12
|
+
number: number;
|
|
13
|
+
url: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const ORIGIN_PAGE = /^origin(-[a-z0-9]+)?\.cursor\.com$/i;
|
|
17
|
+
|
|
18
|
+
function positiveInt(raw: string | undefined): number | undefined {
|
|
19
|
+
if (raw === undefined) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const n = Number(raw);
|
|
23
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return n;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parsed(
|
|
30
|
+
provider: PrProvider,
|
|
31
|
+
owner: string | undefined,
|
|
32
|
+
repo: string | undefined,
|
|
33
|
+
number: number | undefined
|
|
34
|
+
): ParsedPrUrl | undefined {
|
|
35
|
+
if (owner === undefined || repo === undefined || number === undefined) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const url =
|
|
39
|
+
provider === "origin"
|
|
40
|
+
? `https://cursor.com/codebase/${owner}/${repo}/pull/${number}`
|
|
41
|
+
: `https://github.com/${owner}/${repo}/pull/${number}`;
|
|
42
|
+
return { provider, owner, repo, number, url };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Strip Slack mrkdwn wrappers and trailing punctuation before `URL`. */
|
|
46
|
+
function normalizeRaw(raw: string): string {
|
|
47
|
+
return raw
|
|
48
|
+
.trim()
|
|
49
|
+
.replace(/^[<[]+/, "")
|
|
50
|
+
.replace(/[>\].,;]+$/, "");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function parsePrUrl(raw: string): ParsedPrUrl | undefined {
|
|
54
|
+
let url: URL;
|
|
55
|
+
try {
|
|
56
|
+
url = new URL(normalizeRaw(raw));
|
|
57
|
+
} catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
62
|
+
const host = url.hostname.toLowerCase();
|
|
63
|
+
|
|
64
|
+
if (
|
|
65
|
+
(host === "github.com" || host === "www.github.com") &&
|
|
66
|
+
parts.length >= 4 &&
|
|
67
|
+
parts[2] === "pull"
|
|
68
|
+
) {
|
|
69
|
+
return parsed("github", parts[0], parts[1], positiveInt(parts[3]));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (ORIGIN_PAGE.test(host) && parts.length >= 4 && parts[2] === "github") {
|
|
73
|
+
return parsed("origin", parts[0], parts[1], positiveInt(parts[3]));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (
|
|
77
|
+
(host === "cursor.com" || host === "www.cursor.com") &&
|
|
78
|
+
parts[0] === "codebase" &&
|
|
79
|
+
parts.length >= 5 &&
|
|
80
|
+
parts[3] === "pull"
|
|
81
|
+
) {
|
|
82
|
+
return parsed("origin", parts[1], parts[2], positiveInt(parts[4]));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (host === "review.cursor.com" && parts[0] === "pr") {
|
|
86
|
+
if (parts.length >= 5 && parts[3] === "github") {
|
|
87
|
+
return parsed("origin", parts[1], parts[2], positiveInt(parts[4]));
|
|
88
|
+
}
|
|
89
|
+
if (parts.length >= 4) {
|
|
90
|
+
return parsed("origin", parts[1], parts[2], positiveInt(parts[3]));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
type TurnUsage,
|
|
30
30
|
} from "../types.js";
|
|
31
31
|
import { mapSdkTokenUsage, usageTokenSum } from "./cloud-turn-cost.js";
|
|
32
|
+
import type { ConversationMirror } from "./conversation-mirror.js";
|
|
32
33
|
import { resolveApiKeySync } from "./cursor/credentials.js";
|
|
33
34
|
|
|
34
35
|
/** Internal seam between the session engine and the Cursor SDK harness. */
|
|
@@ -117,6 +118,13 @@ export interface CursorSdkRunnerOptions {
|
|
|
117
118
|
* `LocalAgentStore` for heavy multi-session concurrency.
|
|
118
119
|
*/
|
|
119
120
|
store?: LocalAgentStore;
|
|
121
|
+
/**
|
|
122
|
+
* Durable mirror for local conversations (v2 hosted engines). Hydrated
|
|
123
|
+
* before `Agent.resume` so a follow-up landing on a fresh pod finds the
|
|
124
|
+
* conversation, and persisted — awaited — after each local turn settles,
|
|
125
|
+
* because a hosted pod can be parked right after the execution reply.
|
|
126
|
+
*/
|
|
127
|
+
conversationMirror?: ConversationMirror;
|
|
120
128
|
/** Diagnostics for degraded paths (resume-miss fallback). */
|
|
121
129
|
logger?: (line: string) => void;
|
|
122
130
|
/**
|
|
@@ -316,6 +324,13 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
316
324
|
}
|
|
317
325
|
try {
|
|
318
326
|
const result = await run.wait();
|
|
327
|
+
// Mirror the conversation before replying: on hosted v2 the pod can
|
|
328
|
+
// be parked as soon as the execution reply lands, so a
|
|
329
|
+
// fire-and-forget write would race VM teardown. Failed turns still
|
|
330
|
+
// checkpoint the user message, so they persist too.
|
|
331
|
+
if (request.runtime === "local") {
|
|
332
|
+
await this.persistConversation(request, agent.agentId);
|
|
333
|
+
}
|
|
319
334
|
let outcome = toTurnOutcome(result);
|
|
320
335
|
if (outcome.status === "error") {
|
|
321
336
|
// An error result usually means the handle's transport is poisoned
|
|
@@ -469,8 +484,7 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
469
484
|
private async buildLocalOptions(
|
|
470
485
|
request: RunnerPrewarmRequest
|
|
471
486
|
): Promise<AgentOptions> {
|
|
472
|
-
const store =
|
|
473
|
-
this.options.store ?? (await this.getStore(request.runnerStateDir));
|
|
487
|
+
const store = await this.resolveStore(request);
|
|
474
488
|
return {
|
|
475
489
|
...this.buildSharedOptions(request),
|
|
476
490
|
// The built-in tool allowlist rides AgentOptions, not `local`, and is
|
|
@@ -507,6 +521,15 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
507
521
|
}
|
|
508
522
|
|
|
509
523
|
const agentId = request.sdkAgentId ?? request.sessionId;
|
|
524
|
+
const mirror = this.options.conversationMirror;
|
|
525
|
+
if (mirror !== undefined) {
|
|
526
|
+
// Host replacement: restore the conversation from the durable mirror
|
|
527
|
+
// before resuming, so the thread continues instead of starting fresh.
|
|
528
|
+
// A sink failure propagates and fails the turn (retryable) — reading
|
|
529
|
+
// an outage as "nothing mirrored" would silently fork the thread.
|
|
530
|
+
const store = await this.resolveStore(request);
|
|
531
|
+
await mirror.hydrate(store, agentId);
|
|
532
|
+
}
|
|
510
533
|
try {
|
|
511
534
|
return await Agent.resume(agentId, options);
|
|
512
535
|
} catch (error) {
|
|
@@ -570,6 +593,38 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
570
593
|
return Agent.create(options);
|
|
571
594
|
}
|
|
572
595
|
|
|
596
|
+
private resolveStore(
|
|
597
|
+
request: Pick<RunnerPrewarmRequest, "runnerStateDir">
|
|
598
|
+
): Promise<LocalAgentStore> {
|
|
599
|
+
return this.options.store !== undefined
|
|
600
|
+
? Promise.resolve(this.options.store)
|
|
601
|
+
: this.getStore(request.runnerStateDir);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Mirror the settled turn's conversation. Persist failures are logged and
|
|
606
|
+
* dropped — a durable-mirror outage must not fail a turn that already ran.
|
|
607
|
+
*/
|
|
608
|
+
private async persistConversation(
|
|
609
|
+
request: RunnerTurnRequest,
|
|
610
|
+
sdkAgentId: string
|
|
611
|
+
): Promise<void> {
|
|
612
|
+
const mirror = this.options.conversationMirror;
|
|
613
|
+
if (mirror === undefined) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
try {
|
|
617
|
+
const store = await this.resolveStore(request);
|
|
618
|
+
await mirror.persist(store, sdkAgentId);
|
|
619
|
+
} catch (error) {
|
|
620
|
+
this.log(
|
|
621
|
+
`[agent-sdk] conversation mirror: persist for ${sdkAgentId} failed: ${
|
|
622
|
+
error instanceof Error ? error.message : String(error)
|
|
623
|
+
}`
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
573
628
|
private getStore(stateDir: string): Promise<LocalAgentStore> {
|
|
574
629
|
let storePromise = this.stores.get(stateDir);
|
|
575
630
|
if (storePromise === undefined) {
|
package/src/internal/server.ts
CHANGED
|
@@ -51,6 +51,7 @@ import type {
|
|
|
51
51
|
ServeOptions,
|
|
52
52
|
} from "../types.js";
|
|
53
53
|
import { V2EngineError } from "./continuation-identity.js";
|
|
54
|
+
import { selectConversationMirror } from "./conversation-mirror.js";
|
|
54
55
|
import {
|
|
55
56
|
type CursorAccountConnectionRef,
|
|
56
57
|
CursorAccountMcpBridge,
|
|
@@ -516,12 +517,21 @@ export async function startServer(
|
|
|
516
517
|
"[agent-sdk] warning: --allow-anonymous admits every HTTP caller as the same principal; use only on a trusted network (e.g. Tailscale) and prefer --bearer-token for shared hosts"
|
|
517
518
|
);
|
|
518
519
|
}
|
|
520
|
+
// v2 hosted engines mirror the SDK conversation store to the deployment's
|
|
521
|
+
// shared file storage so a fresh pod resumes threads instead of forking
|
|
522
|
+
// them. Hosted serve is single-agent; a multi-mount serve (dev) has no
|
|
523
|
+
// single agent name to mint keys under and does not mirror.
|
|
524
|
+
const conversationMirror =
|
|
525
|
+
mounts.length === 1 && mounts[0] !== undefined
|
|
526
|
+
? selectConversationMirror({ project: mounts[0].project, logger })
|
|
527
|
+
: undefined;
|
|
519
528
|
const sdkRunner =
|
|
520
529
|
options.runner ??
|
|
521
530
|
new CursorSdkRunner({
|
|
522
531
|
...(options.apiKey === undefined || options.apiKey.trim() === ""
|
|
523
532
|
? {}
|
|
524
533
|
: { apiKey: options.apiKey }),
|
|
534
|
+
...(conversationMirror === undefined ? {} : { conversationMirror }),
|
|
525
535
|
logger,
|
|
526
536
|
});
|
|
527
537
|
// Grok Bot turns run on Cursor's hosted Grok Bot (Sand) harness through
|
|
@@ -113,6 +113,10 @@ import {
|
|
|
113
113
|
createHostSlackClient,
|
|
114
114
|
type HostPlatforms,
|
|
115
115
|
} from "./host-platforms.js";
|
|
116
|
+
import {
|
|
117
|
+
HostedAdmissionLocalSendError,
|
|
118
|
+
isDuringHostedAdmission,
|
|
119
|
+
} from "./hosted-admission-context.js";
|
|
116
120
|
import {
|
|
117
121
|
formatContinuationToken,
|
|
118
122
|
newContinuationKey,
|
|
@@ -465,6 +469,7 @@ export class SessionEngine {
|
|
|
465
469
|
// for why the choice does not belong to the agent author.
|
|
466
470
|
const frameworkStorage = selectFrameworkStorageDefinition({
|
|
467
471
|
authored: options.project.storage,
|
|
472
|
+
architecture: options.project.agent.architecture,
|
|
468
473
|
});
|
|
469
474
|
this.storage =
|
|
470
475
|
frameworkStorage === undefined
|
|
@@ -811,6 +816,13 @@ export class SessionEngine {
|
|
|
811
816
|
message: string,
|
|
812
817
|
options: EngineSendOptions = {}
|
|
813
818
|
): Promise<ChannelSession> {
|
|
819
|
+
if (isDuringHostedAdmission()) {
|
|
820
|
+
// Only sends that bypassed the admission capture stub (typically a
|
|
821
|
+
// startup-captured `onStart` send) reach the engine inside the
|
|
822
|
+
// admission window; running one would leak an invisible local session
|
|
823
|
+
// on a sessionless pod.
|
|
824
|
+
throw new HostedAdmissionLocalSendError();
|
|
825
|
+
}
|
|
814
826
|
const identity =
|
|
815
827
|
channelId === HTTP_CHANNEL_ID
|
|
816
828
|
? httpContinuationIdentity(this.project.httpChannel)
|
|
@@ -74,8 +74,13 @@ export interface CursorHostedStorageOptions {
|
|
|
74
74
|
* `https://api.cursor.com`).
|
|
75
75
|
*/
|
|
76
76
|
baseUrl?: string;
|
|
77
|
-
/**
|
|
78
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Bearer credential (default: `CURSOR_API_KEY`). A function is
|
|
79
|
+
* re-resolved on every call: hosted freeze snapshots listen unsigned and
|
|
80
|
+
* bind the key file after serve start, so a value captured at construct
|
|
81
|
+
* time would be permanently absent.
|
|
82
|
+
*/
|
|
83
|
+
apiKey?: string | (() => string | undefined);
|
|
79
84
|
/** Injected for tests. */
|
|
80
85
|
fetch?: typeof fetch;
|
|
81
86
|
}
|
|
@@ -180,7 +185,9 @@ function toCasResult(response: KvCasResponse): KvCasResult {
|
|
|
180
185
|
}
|
|
181
186
|
|
|
182
187
|
function resolveApiKey(options: CursorHostedStorageOptions): string {
|
|
183
|
-
const
|
|
188
|
+
const configured =
|
|
189
|
+
typeof options.apiKey === "function" ? options.apiKey() : options.apiKey;
|
|
190
|
+
const key = (configured ?? process.env.CURSOR_API_KEY)?.trim();
|
|
184
191
|
if (key === undefined || key === "") {
|
|
185
192
|
throw new Error(
|
|
186
193
|
"cursorHostedStorage requires CURSOR_API_KEY (deployment pod credential on Cursor hosting)"
|
|
@@ -58,7 +58,7 @@ policy only) opts paths out entirely.
|
|
|
58
58
|
- One model turn per PR head assesses the diff against the base rubric
|
|
59
59
|
(`agent/lib/owners/prompts/base-review.md`) plus matched rule prompts.
|
|
60
60
|
- The model records a verdict with `record_review`; the host recomputes
|
|
61
|
-
the policy and applies the deterministic decision
|
|
61
|
+
the policy and applies the deterministic decision: approve, or request
|
|
62
62
|
the policy owners (max 2, never the author) and leave one comment it
|
|
63
63
|
keeps updated.
|
|
64
64
|
- The model never writes to GitHub. Broken policy files fail closed with
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { defineAgent } from "@cursor/july";
|
|
4
2
|
|
|
5
3
|
export default defineAgent({
|
|
6
|
-
model: {
|
|
7
|
-
id: "grok-4.5",
|
|
8
|
-
params: [
|
|
9
|
-
{ id: "effort", value: "high" },
|
|
10
|
-
{ id: "fast", value: "true" },
|
|
11
|
-
],
|
|
12
|
-
},
|
|
13
4
|
tools: ["read", "grep", "glob", "ls"],
|
|
14
5
|
local: {
|
|
15
6
|
sandbox: true,
|
|
16
|
-
cwd: join(homedir(), ".cache", "agent-serve", "agentic-owners"),
|
|
17
7
|
},
|
|
18
8
|
});
|
|
@@ -22,10 +22,8 @@ const REVIEW_ACTIONS = new Set([
|
|
|
22
22
|
let log: (line: string) => void = () => {};
|
|
23
23
|
|
|
24
24
|
export default githubChannel({
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// GitHub App setup. Empty repos boots without the binding so a fresh
|
|
28
|
-
// scaffold's `dev` / `eval` run; use `agent-sdk github replay` there.
|
|
25
|
+
// Empty `repos` skips the Cursor GitHub binding so `dev` / `eval` boot
|
|
26
|
+
// on a fresh scaffold.
|
|
29
27
|
cursorAccount: config.repos.length > 0 ? { repos: config.repos } : undefined,
|
|
30
28
|
// The model never writes to GitHub; record_review actuates host-side.
|
|
31
29
|
deliverReplies: false,
|
|
@@ -33,9 +31,7 @@ export default githubChannel({
|
|
|
33
31
|
log = (line) => args.logger(`[agentic-owners] ${line}`);
|
|
34
32
|
},
|
|
35
33
|
onPullRequest: async (ctx, pr) => {
|
|
36
|
-
// A base retarget re-resolves
|
|
37
|
-
// and the governing policy files both changed, so the old verdict — and
|
|
38
|
-
// any approval it produced — no longer covers this PR. The fresh
|
|
34
|
+
// A base retarget re-resolves policy against the new base. The fresh
|
|
39
35
|
// actuation dismisses a leftover approve when the new decision holds.
|
|
40
36
|
const baseRetargeted =
|
|
41
37
|
pr.action === "edited" && pr.baseRefChangedFrom !== undefined;
|
|
@@ -54,9 +50,8 @@ export default githubChannel({
|
|
|
54
50
|
try {
|
|
55
51
|
const state = await loadReviewState(forge, log);
|
|
56
52
|
if (!state.enrolled) {
|
|
57
|
-
// Enrollment can end while an agent approval is live
|
|
58
|
-
//
|
|
59
|
-
// toward branch protection under a policy that never granted it.
|
|
53
|
+
// Enrollment can end while an agent approval is live; dismiss it
|
|
54
|
+
// instead of leaving it counting toward branch protection.
|
|
60
55
|
return {
|
|
61
56
|
task: () =>
|
|
62
57
|
dismissStaleApprovals(
|
|
@@ -69,14 +64,10 @@ export default githubChannel({
|
|
|
69
64
|
auth: defaultGitHubAuth(ctx),
|
|
70
65
|
message: renderReviewWake(state),
|
|
71
66
|
title: `Review ${ctx.repository.fullName}#${pr.number}`,
|
|
72
|
-
// Freeze the assessed head host-side: record_review reads this file
|
|
73
|
-
// instead of trusting a model-supplied SHA, so a mid-turn push (or a
|
|
74
|
-
// prompt-injected SHA) can never line up with the live head.
|
|
75
67
|
workspaceFiles: { "pr/head.sha": state.pr.headSha },
|
|
76
68
|
};
|
|
77
69
|
} catch (error) {
|
|
78
70
|
if (error instanceof AgenticOwnersConfigValidationError) {
|
|
79
|
-
// Fail closed but visibly: comment the issues, review nothing.
|
|
80
71
|
return { task: () => reportPolicyError(forge, error) };
|
|
81
72
|
}
|
|
82
73
|
throw error;
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The one file to edit: which GitHub repositories the agent reviews.
|
|
3
|
-
*
|
|
4
|
-
* Setting it streams PR events and a repo-scoped GitHub credential through
|
|
5
|
-
* your signed-in Cursor account (`agent-sdk login`) — no webhook or GitHub
|
|
6
|
-
* App setup. While empty, the agent still runs locally against replayed
|
|
7
|
-
* PRs (`agent-sdk github replay <pr-url>`).
|
|
8
|
-
*/
|
|
9
1
|
const config = {
|
|
10
2
|
repos: [] as string[], // e.g. ["acme/api"]
|
|
11
3
|
};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
// Policy
|
|
2
|
-
//
|
|
3
|
-
// **base ref**, so a PR cannot exempt itself by editing its own governance
|
|
4
|
-
// files. Mirrors the Agentic Owners pipeline's `resolvePolicyForPr`, minus
|
|
5
|
-
// the linguist-generated partition and content caching.
|
|
1
|
+
// Policy files and rule prompts are read from the PR's base ref so a PR
|
|
2
|
+
// cannot exempt itself by editing its own governance files.
|
|
6
3
|
|
|
7
4
|
import {
|
|
8
5
|
decideApproval,
|
|
@@ -297,12 +294,6 @@ export async function loadReviewState(
|
|
|
297
294
|
};
|
|
298
295
|
}
|
|
299
296
|
|
|
300
|
-
/**
|
|
301
|
-
* The model turn's wake message: composed rubric, PR context, and the
|
|
302
|
-
* host-fetched diff. Fenced so author-controlled patch bytes cannot escape
|
|
303
|
-
* into the trusted markdown (the fence outruns any backtick run inside the
|
|
304
|
-
* patch).
|
|
305
|
-
*/
|
|
306
297
|
/**
|
|
307
298
|
* Filenames are author-controlled and render outside the diff fence: strip
|
|
308
299
|
* control characters and backticks so a hostile path cannot fabricate
|
|
@@ -648,9 +639,6 @@ export async function actuateReview(input: {
|
|
|
648
639
|
}).filter(
|
|
649
640
|
(handle) => normalizeHandle(handle) !== normalizeHandle(pr.authorLogin ?? "")
|
|
650
641
|
);
|
|
651
|
-
// The full decision — policy verdict plus the governance / evidence
|
|
652
|
-
// fail-closes — is computed in one place; from here on this function
|
|
653
|
-
// only writes.
|
|
654
642
|
const decision = decideApproval({
|
|
655
643
|
matchedRules: state.matchedRules,
|
|
656
644
|
riskLevel: verdict.riskLevel,
|
|
@@ -742,7 +730,6 @@ export async function actuateReview(input: {
|
|
|
742
730
|
const alreadyRequested = new Set(
|
|
743
731
|
pr.requestedReviewers.map(normalizeHandle)
|
|
744
732
|
);
|
|
745
|
-
// Adds fill only the remaining slots under the max-of-two rule.
|
|
746
733
|
const newReviewers = decision.requestReviewers
|
|
747
734
|
.filter((handle) => !alreadyRequested.has(normalizeHandle(handle)))
|
|
748
735
|
.slice(
|
|
@@ -41,10 +41,8 @@ export default defineTool({
|
|
|
41
41
|
"not a GitHub pull-request session — reviews start from the github channel (agent-sdk github replay <pr-url> to test locally)",
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// mid-turn push can never line up with the live head: the actuation
|
|
47
|
-
// compares wake-observed against live and skips on any mismatch.
|
|
44
|
+
// Wake froze the assessed head into the session workspace. A model-supplied
|
|
45
|
+
// SHA is never trusted.
|
|
48
46
|
let assessedHeadSha: string;
|
|
49
47
|
try {
|
|
50
48
|
assessedHeadSha = (
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { defineAgent } from "@cursor/july";
|
|
4
2
|
|
|
5
3
|
export default defineAgent({
|
|
6
|
-
model: {
|
|
7
|
-
id: "grok-4.5",
|
|
8
|
-
params: [
|
|
9
|
-
{ id: "effort", value: "high" },
|
|
10
|
-
{ id: "fast", value: "true" },
|
|
11
|
-
],
|
|
12
|
-
},
|
|
13
4
|
tools: ["read", "grep"],
|
|
14
5
|
local: {
|
|
15
6
|
sandbox: true,
|
|
16
|
-
cwd: join(homedir(), ".cache", "agent-serve", "demo"),
|
|
17
7
|
},
|
|
18
8
|
});
|
|
@@ -34,8 +34,6 @@ enables auto-merge, or force-pushes.
|
|
|
34
34
|
GitHub wakes (`cursorAccount`) resume an already-driven PR on comment,
|
|
35
35
|
review, and failing CI. They do not start a session on `pull_request.opened`.
|
|
36
36
|
|
|
37
|
-
Affinity and the webhook buffer live in `host.kv` (`agent/storage.ts`).
|
|
38
|
-
|
|
39
37
|
## Files to edit
|
|
40
38
|
|
|
41
39
|
- `agent/lib/repos.ts`: repos Cursor relays SCM events for
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
import { homedir } from "node:os";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { defineAgent } from "@cursor/july";
|
|
4
2
|
|
|
5
3
|
export default defineAgent({
|
|
6
|
-
model: {
|
|
7
|
-
id: "grok-4.5",
|
|
8
|
-
params: [
|
|
9
|
-
{ id: "effort", value: "high" },
|
|
10
|
-
{ id: "fast", value: "true" },
|
|
11
|
-
],
|
|
12
|
-
},
|
|
13
|
-
runtime: "local",
|
|
14
4
|
local: {
|
|
15
5
|
sandbox: true,
|
|
16
|
-
cwd: join(homedir(), ".cache", "agent-serve", "pr-autofixer"),
|
|
17
6
|
},
|
|
18
7
|
cloud: {
|
|
19
8
|
env: { type: "cloud" },
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { slackChannel } from "@cursor/july/channels/slack";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Slack channel (Socket Mode).
|
|
5
|
-
*
|
|
6
|
-
* Tokens: PR_AUTOFIXER_SLACK_BOT_TOKEN + PR_AUTOFIXER_SLACK_APP_TOKEN
|
|
7
|
-
*
|
|
8
|
-
* Full setup: agent-sdk slack create
|
|
9
|
-
*/
|
|
10
3
|
export default slackChannel({
|
|
11
4
|
envPrefix: "PR_AUTOFIXER",
|
|
12
5
|
suggestedPromptsTitle: "Try asking",
|