@cursor/july 0.1.31 → 0.1.33
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/docs/404.html +1 -1
- package/dist/docs/ab.html +2 -2
- package/dist/docs/assets/{app.DigB_9cQ.js → app.CgZqLyWL.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.B1uHGxBZ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BCMX25Bv.js → VPLocalSearchBox.C4KigoQn.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BDDWeELx.js → theme.C_Ty4yT7.js} +2 -2
- package/dist/docs/building-with-agents.html +2 -2
- package/dist/docs/concepts.html +2 -2
- package/dist/docs/deployment.html +2 -2
- package/dist/docs/evals.html +2 -2
- package/dist/docs/example-agents/approval-buddy.html +2 -2
- package/dist/docs/example-agents/benny.html +2 -2
- package/dist/docs/example-agents/bugbot.html +2 -2
- package/dist/docs/example-agents/codebase-wiki.html +2 -2
- package/dist/docs/example-agents/codeowners-review.html +2 -2
- package/dist/docs/example-agents/concierge.html +2 -2
- package/dist/docs/example-agents/fsd.html +2 -2
- package/dist/docs/example-agents/index.html +2 -2
- package/dist/docs/example-agents/knowledge-base.html +2 -2
- package/dist/docs/example-agents/oncall.html +2 -2
- package/dist/docs/example-agents/security-reviewer.html +2 -2
- package/dist/docs/example-agents/slack-agent.html +2 -2
- package/dist/docs/example-agents/weather-agent.html +2 -2
- package/dist/docs/guides/agent-to-agent.html +2 -2
- package/dist/docs/guides/cloud-runtime.html +2 -2
- package/dist/docs/guides/github.html +2 -2
- package/dist/docs/guides/human-in-the-loop.html +2 -2
- package/dist/docs/guides/mcp-oauth.html +2 -2
- package/dist/docs/guides/slack.html +2 -2
- package/dist/docs/guides/webhooks.html +2 -2
- package/dist/docs/hillclimbing.html +2 -2
- package/dist/docs/index.html +2 -2
- package/dist/docs/quickstart.html +2 -2
- package/dist/docs/reference/agent-config.html +2 -2
- package/dist/docs/reference/channels.html +2 -2
- package/dist/docs/reference/cli.html +2 -2
- package/dist/docs/reference/connections.html +2 -2
- package/dist/docs/reference/hooks.html +2 -2
- package/dist/docs/reference/http-api.html +2 -2
- package/dist/docs/reference/instructions.html +2 -2
- package/dist/docs/reference/playground.html +2 -2
- package/dist/docs/reference/project-layout.html +2 -2
- package/dist/docs/reference/prompt.html +2 -2
- package/dist/docs/reference/schedules.html +2 -2
- package/dist/docs/reference/sessions.html +2 -2
- package/dist/docs/reference/skills.html +2 -2
- package/dist/docs/reference/subagents.html +2 -2
- package/dist/docs/reference/tools.html +2 -2
- package/dist/docs/scaffolding-agents.html +2 -2
- package/dist/docs/storage.html +2 -2
- package/dist/docs/troubleshooting.html +2 -2
- package/dist/files-backends/agent-store-presigned-url.d.ts +9 -98
- package/dist/files-backends/agent-store-presigned-url.d.ts.map +1 -1
- package/dist/files-backends/agent-store-presigned-url.js +199 -314
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +25 -2
- package/dist/internal/cursor-relay-core.d.ts +12 -0
- package/dist/internal/cursor-relay-core.d.ts.map +1 -1
- package/dist/internal/cursor-relay-core.js +42 -8
- package/dist/internal/review-comments.d.ts +9 -1
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +76 -0
- package/dist/internal/server.js +8 -1
- package/dist/playground/assets/index-BRhBbnd4.css +1 -0
- package/dist/playground/assets/index-CVlCAcUx.js +85 -0
- package/dist/playground/index.html +2 -2
- package/package.json +3 -23
- package/src/files-backends/agent-store-presigned-url.ts +9 -401
- package/src/files-backends/cursor-hosted.ts +32 -6
- package/src/internal/cursor-relay-core.ts +62 -3
- package/src/internal/review-comments.ts +102 -2
- package/src/internal/server.ts +9 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.CnFFl07y.js +0 -1
- package/dist/playground/assets/index-50PKeJlG.css +0 -1
- package/dist/playground/assets/index-C0f2Wl1q.js +0 -85
|
@@ -151,6 +151,42 @@ export interface CursorRelayCoreOptions<TEvent extends { offset: string }> {
|
|
|
151
151
|
fetchImpl?: typeof fetch;
|
|
152
152
|
/** Test seam: override long-poll wait (default 25s). */
|
|
153
153
|
pollWaitMs?: number;
|
|
154
|
+
/**
|
|
155
|
+
* Desired-revision generation for hosted engines. Sent as
|
|
156
|
+
* `x-agent-serve-generation` so the control plane can fence zombies after
|
|
157
|
+
* a redeploy. Defaults to `process.env.AGENT_SERVE_GENERATION` when set.
|
|
158
|
+
*/
|
|
159
|
+
deploymentGeneration?: number;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Must match server-shared `AGENT_SERVE_GENERATION_HEADER`. */
|
|
163
|
+
export const AGENT_SERVE_GENERATION_HEADER = "x-agent-serve-generation";
|
|
164
|
+
|
|
165
|
+
/** Must match server-shared `AGENT_SERVE_GENERATION_ENV_VAR`. */
|
|
166
|
+
export const AGENT_SERVE_GENERATION_ENV_VAR = "AGENT_SERVE_GENERATION";
|
|
167
|
+
|
|
168
|
+
export function resolveAgentServeDeploymentGeneration(
|
|
169
|
+
explicit?: number
|
|
170
|
+
): number | undefined {
|
|
171
|
+
if (
|
|
172
|
+
explicit !== undefined &&
|
|
173
|
+
Number.isSafeInteger(explicit) &&
|
|
174
|
+
explicit > 0
|
|
175
|
+
) {
|
|
176
|
+
return explicit;
|
|
177
|
+
}
|
|
178
|
+
const raw = process.env[AGENT_SERVE_GENERATION_ENV_VAR]?.trim();
|
|
179
|
+
if (raw === undefined || raw.length === 0) {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
if (!/^\d+$/.test(raw)) {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
const parsed = Number(raw);
|
|
186
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) {
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
return parsed;
|
|
154
190
|
}
|
|
155
191
|
|
|
156
192
|
export class CursorRelayCore<TEvent extends { offset: string }> {
|
|
@@ -160,6 +196,7 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
|
|
|
160
196
|
private consumePromise: Promise<void> | undefined;
|
|
161
197
|
private readonly offsetStore: RelayOffsetStore;
|
|
162
198
|
private readonly fetchImpl: typeof fetch;
|
|
199
|
+
private readonly deploymentGeneration: number | undefined;
|
|
163
200
|
private consumerId = "";
|
|
164
201
|
/** undefined until the first registration, so a clean first success logs. */
|
|
165
202
|
private lastRegistrationLog: string | undefined;
|
|
@@ -175,6 +212,9 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
|
|
|
175
212
|
constructor(private readonly options: CursorRelayCoreOptions<TEvent>) {
|
|
176
213
|
this.offsetStore = new RelayOffsetStore(options.stateDir);
|
|
177
214
|
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
215
|
+
this.deploymentGeneration = resolveAgentServeDeploymentGeneration(
|
|
216
|
+
options.deploymentGeneration
|
|
217
|
+
);
|
|
178
218
|
}
|
|
179
219
|
|
|
180
220
|
start(): void {
|
|
@@ -285,6 +325,13 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
|
|
|
285
325
|
headers: {
|
|
286
326
|
authorization: `Bearer ${this.options.apiKey}`,
|
|
287
327
|
"content-type": "application/json",
|
|
328
|
+
...(this.deploymentGeneration === undefined
|
|
329
|
+
? {}
|
|
330
|
+
: {
|
|
331
|
+
[AGENT_SERVE_GENERATION_HEADER]: String(
|
|
332
|
+
this.deploymentGeneration
|
|
333
|
+
),
|
|
334
|
+
}),
|
|
288
335
|
},
|
|
289
336
|
body: JSON.stringify(adapter.buildDeregistrationBody(this.consumerId)),
|
|
290
337
|
signal: controller.signal,
|
|
@@ -322,6 +369,13 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
|
|
|
322
369
|
headers: {
|
|
323
370
|
authorization: `Bearer ${this.options.apiKey}`,
|
|
324
371
|
...(body === undefined ? {} : { "content-type": "application/json" }),
|
|
372
|
+
...(this.deploymentGeneration === undefined
|
|
373
|
+
? {}
|
|
374
|
+
: {
|
|
375
|
+
[AGENT_SERVE_GENERATION_HEADER]: String(
|
|
376
|
+
this.deploymentGeneration
|
|
377
|
+
),
|
|
378
|
+
}),
|
|
325
379
|
},
|
|
326
380
|
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
327
381
|
...(this.abort === undefined ? {} : { signal: this.abort.signal }),
|
|
@@ -514,14 +568,19 @@ export class CursorRelayCore<TEvent extends { offset: string }> {
|
|
|
514
568
|
response: Response,
|
|
515
569
|
backoffMs: number
|
|
516
570
|
): Promise<number> {
|
|
517
|
-
|
|
518
|
-
|
|
571
|
+
// 409 = generation fence (stale resident after redeploy). Same long
|
|
572
|
+
// backoff as auth: retrying every second cannot heal a wrong generation.
|
|
573
|
+
const isAuthOrFenceFailure =
|
|
574
|
+
response.status === 401 ||
|
|
575
|
+
response.status === 403 ||
|
|
576
|
+
response.status === 409;
|
|
577
|
+
const sleepMs = isAuthOrFenceFailure ? AUTH_FAILURE_BACKOFF_MS : backoffMs;
|
|
519
578
|
const text = await response.text().catch(() => "");
|
|
520
579
|
this.log(
|
|
521
580
|
`poll failed: ${response.status} ${text.slice(0, 300)} (retrying in ${Math.round(sleepMs / 1000)}s)`
|
|
522
581
|
);
|
|
523
582
|
await this.sleep(sleepMs);
|
|
524
|
-
return
|
|
583
|
+
return isAuthOrFenceFailure
|
|
525
584
|
? AUTH_FAILURE_BACKOFF_MS
|
|
526
585
|
: Math.min(backoffMs * 2, MAX_BACKOFF_MS);
|
|
527
586
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import type { HostGitHubClient } from "../types.js";
|
|
2
3
|
|
|
3
|
-
export type ReviewCommentPublisher = "local_gh";
|
|
4
|
+
export type ReviewCommentPublisher = "local_gh" | "host_github";
|
|
4
5
|
|
|
5
6
|
export type ReviewCommentPostResult = {
|
|
6
7
|
posted: boolean;
|
|
@@ -84,6 +85,80 @@ export function createLocalGhReviewPublisher(): ReviewCommentPublisherImplementa
|
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Posts a PR review through the host GitHub client (Cursor account lease,
|
|
90
|
+
* App installation token, or PAT). Prefer this on Cursor-deployed agents —
|
|
91
|
+
* no separate GitHub write secret is required when `githubChannel({
|
|
92
|
+
* cursorAccount })` is bound.
|
|
93
|
+
*/
|
|
94
|
+
export function createHostGithubReviewPublisher(
|
|
95
|
+
github: HostGitHubClient
|
|
96
|
+
): ReviewCommentPublisherImplementation {
|
|
97
|
+
return {
|
|
98
|
+
async postReview(args) {
|
|
99
|
+
return postReviewViaHostGithub(github, args);
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function postReviewViaHostGithub(
|
|
105
|
+
github: HostGitHubClient,
|
|
106
|
+
args: ReviewCommentPostArgs
|
|
107
|
+
): Promise<ReviewCommentPostResult> {
|
|
108
|
+
const prepared = prepareReviewCommentsRequest(args);
|
|
109
|
+
if (prepared.kind === "skipped") {
|
|
110
|
+
return {
|
|
111
|
+
...prepared.result,
|
|
112
|
+
publisher: "host_github",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { request } = prepared;
|
|
117
|
+
const [owner, repo] = request.repo.split("/", 2);
|
|
118
|
+
if (owner === undefined || repo === undefined || repo === "") {
|
|
119
|
+
return skippedHost("unparseable owner/repo from PR URL");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
args.signal?.throwIfAborted();
|
|
124
|
+
const octokit = await github.getOctokit();
|
|
125
|
+
args.signal?.throwIfAborted();
|
|
126
|
+
const response = await octokit.rest.pulls.createReview({
|
|
127
|
+
owner,
|
|
128
|
+
repo,
|
|
129
|
+
pull_number: request.prNumber,
|
|
130
|
+
commit_id: request.headSha,
|
|
131
|
+
body: request.message,
|
|
132
|
+
event: "COMMENT",
|
|
133
|
+
comments: request.comments.map(toGithubPullRequestReviewComment),
|
|
134
|
+
});
|
|
135
|
+
const htmlUrl =
|
|
136
|
+
typeof response.data.html_url === "string"
|
|
137
|
+
? response.data.html_url
|
|
138
|
+
: undefined;
|
|
139
|
+
const inlineCommentCount = request.comments.length;
|
|
140
|
+
return {
|
|
141
|
+
posted: true,
|
|
142
|
+
publisher: "host_github",
|
|
143
|
+
mode:
|
|
144
|
+
inlineCommentCount > 0
|
|
145
|
+
? "review_with_inline_comments"
|
|
146
|
+
: "review_body_only",
|
|
147
|
+
inlineCommentCount,
|
|
148
|
+
detail: htmlUrl
|
|
149
|
+
? `posted via host GitHub: ${htmlUrl}`
|
|
150
|
+
: `posted via host GitHub with ${inlineCommentCount} inline comment(s)`,
|
|
151
|
+
};
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (args.signal?.aborted) {
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
return skippedHost(
|
|
157
|
+
`host GitHub review post failed: ${error instanceof Error ? error.message : String(error)}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
87
162
|
async function postReviewViaLocalGh(
|
|
88
163
|
args: ReviewCommentPostArgs
|
|
89
164
|
): Promise<ReviewCommentPostResult> {
|
|
@@ -129,10 +204,25 @@ async function postReviewViaLocalGh(
|
|
|
129
204
|
}
|
|
130
205
|
}
|
|
131
206
|
|
|
207
|
+
type GithubPullRequestReviewComment = {
|
|
208
|
+
path: string;
|
|
209
|
+
body: string;
|
|
210
|
+
line: number;
|
|
211
|
+
side: "RIGHT";
|
|
212
|
+
start_line?: number;
|
|
213
|
+
start_side?: "RIGHT";
|
|
214
|
+
};
|
|
215
|
+
|
|
132
216
|
function toGithubReviewComment(
|
|
133
217
|
comment: ReviewInlineComment
|
|
134
218
|
): Record<string, string | number> {
|
|
135
|
-
|
|
219
|
+
return toGithubPullRequestReviewComment(comment);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function toGithubPullRequestReviewComment(
|
|
223
|
+
comment: ReviewInlineComment
|
|
224
|
+
): GithubPullRequestReviewComment {
|
|
225
|
+
const body: GithubPullRequestReviewComment = {
|
|
136
226
|
path: comment.file,
|
|
137
227
|
body: comment.body,
|
|
138
228
|
line: comment.line,
|
|
@@ -159,6 +249,16 @@ function skipped(detail: string): ReviewCommentPostResult {
|
|
|
159
249
|
};
|
|
160
250
|
}
|
|
161
251
|
|
|
252
|
+
function skippedHost(detail: string): ReviewCommentPostResult {
|
|
253
|
+
return {
|
|
254
|
+
posted: false,
|
|
255
|
+
publisher: "host_github",
|
|
256
|
+
mode: "skipped",
|
|
257
|
+
inlineCommentCount: 0,
|
|
258
|
+
detail,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
162
262
|
function parseGithubPrUrl(
|
|
163
263
|
prUrl: string
|
|
164
264
|
):
|
package/src/internal/server.ts
CHANGED
|
@@ -959,7 +959,15 @@ function mountAgentRoutes(
|
|
|
959
959
|
method: "GET",
|
|
960
960
|
pattern: `${base}/v1/health`,
|
|
961
961
|
auth: "none",
|
|
962
|
-
|
|
962
|
+
// Temporal consumes this response before promoting a newly deployed pod.
|
|
963
|
+
// Include the framework build there so deploy observability gets the
|
|
964
|
+
// version from the process that will serve turns, with no second request.
|
|
965
|
+
handler: () =>
|
|
966
|
+
jsonResponse({
|
|
967
|
+
ok: true,
|
|
968
|
+
name: project.name,
|
|
969
|
+
version: packageVersion(),
|
|
970
|
+
}),
|
|
963
971
|
});
|
|
964
972
|
|
|
965
973
|
// Bootstrap the SPA can't derive from its own URL (agent name, dev mode, and
|