@cursor/july 0.1.74 → 0.1.76

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 (140) hide show
  1. package/dist/channels/checks.d.ts +111 -0
  2. package/dist/channels/checks.d.ts.map +1 -0
  3. package/dist/channels/checks.js +86 -0
  4. package/dist/channels/deployments/deployments-channel.d.ts +44 -0
  5. package/dist/channels/deployments/deployments-channel.d.ts.map +1 -0
  6. package/dist/channels/deployments/deployments-channel.js +92 -0
  7. package/dist/channels/deployments/index.d.ts +26 -0
  8. package/dist/channels/deployments/index.d.ts.map +1 -0
  9. package/dist/channels/deployments/index.js +25 -0
  10. package/dist/channels/deployments/types.d.ts +69 -0
  11. package/dist/channels/deployments/types.d.ts.map +1 -0
  12. package/dist/channels/deployments/types.js +6 -0
  13. package/dist/channels/github/checks.d.ts +13 -48
  14. package/dist/channels/github/checks.d.ts.map +1 -1
  15. package/dist/channels/github/checks.js +56 -64
  16. package/dist/channels/origin/api.d.ts +100 -0
  17. package/dist/channels/origin/api.d.ts.map +1 -0
  18. package/dist/channels/origin/api.js +225 -0
  19. package/dist/channels/origin/binding.d.ts +44 -0
  20. package/dist/channels/origin/binding.d.ts.map +1 -0
  21. package/dist/channels/origin/binding.js +45 -0
  22. package/dist/channels/origin/checks.d.ts +107 -0
  23. package/dist/channels/origin/checks.d.ts.map +1 -0
  24. package/dist/channels/origin/checks.js +145 -0
  25. package/dist/channels/origin/index.d.ts +3 -0
  26. package/dist/channels/origin/index.d.ts.map +1 -1
  27. package/dist/channels/origin/index.js +3 -0
  28. package/dist/channels/origin/origin-channel.d.ts +4 -4
  29. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  30. package/dist/channels/origin/origin-channel.js +12 -2
  31. package/dist/channels/origin/types.d.ts +36 -0
  32. package/dist/channels/origin/types.d.ts.map +1 -1
  33. package/dist/channels.d.ts +1 -0
  34. package/dist/channels.d.ts.map +1 -1
  35. package/dist/channels.js +1 -0
  36. package/dist/docs/404.html +2 -2
  37. package/dist/docs/ab.html +3 -3
  38. package/dist/docs/assets/{app.BETd-WqU.js → app.b8bhB2SV.js} +1 -1
  39. package/dist/docs/assets/chunks/@localSearchIndexroot.DGZeXVqn.js +1 -0
  40. package/dist/docs/assets/chunks/{VPLocalSearchBox.wEaGEmV9.js → VPLocalSearchBox.lLSeIyWw.js} +1 -1
  41. package/dist/docs/assets/chunks/{theme.DNDicFOP.js → theme.C_owesa-.js} +2 -2
  42. package/dist/docs/assets/{reference_channels.md.MN8iDy8V.js → reference_channels.md.G5GysZ7e.js} +2 -2
  43. package/dist/docs/assets/{reference_channels.md.MN8iDy8V.lean.js → reference_channels.md.G5GysZ7e.lean.js} +1 -1
  44. package/dist/docs/building-with-agents.html +3 -3
  45. package/dist/docs/concepts.html +3 -3
  46. package/dist/docs/deployment.html +3 -3
  47. package/dist/docs/evals.html +3 -3
  48. package/dist/docs/example-agents/approval-buddy.html +3 -3
  49. package/dist/docs/example-agents/benny.html +3 -3
  50. package/dist/docs/example-agents/bugbot.html +3 -3
  51. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  52. package/dist/docs/example-agents/codeowners-review.html +3 -3
  53. package/dist/docs/example-agents/concierge.html +3 -3
  54. package/dist/docs/example-agents/fsd.html +3 -3
  55. package/dist/docs/example-agents/index.html +3 -3
  56. package/dist/docs/example-agents/knowledge-base.html +3 -3
  57. package/dist/docs/example-agents/oncall.html +3 -3
  58. package/dist/docs/example-agents/security-reviewer.html +3 -3
  59. package/dist/docs/example-agents/slack-agent.html +3 -3
  60. package/dist/docs/example-agents/weather-agent.html +3 -3
  61. package/dist/docs/guides/agent-to-agent.html +3 -3
  62. package/dist/docs/guides/cloud-runtime.html +3 -3
  63. package/dist/docs/guides/convert-automation.html +3 -3
  64. package/dist/docs/guides/github.html +3 -3
  65. package/dist/docs/guides/human-in-the-loop.html +3 -3
  66. package/dist/docs/guides/mcp-oauth.html +3 -3
  67. package/dist/docs/guides/opentelemetry.html +3 -3
  68. package/dist/docs/guides/slack.html +3 -3
  69. package/dist/docs/guides/webhooks.html +3 -3
  70. package/dist/docs/hashmap.json +1 -1
  71. package/dist/docs/hillclimbing.html +3 -3
  72. package/dist/docs/index.html +3 -3
  73. package/dist/docs/quickstart.html +3 -3
  74. package/dist/docs/reference/agent-config.html +3 -3
  75. package/dist/docs/reference/artifacts.html +3 -3
  76. package/dist/docs/reference/channels.html +5 -5
  77. package/dist/docs/reference/cli.html +3 -3
  78. package/dist/docs/reference/connections.html +3 -3
  79. package/dist/docs/reference/hooks.html +3 -3
  80. package/dist/docs/reference/http-api.html +3 -3
  81. package/dist/docs/reference/instructions.html +3 -3
  82. package/dist/docs/reference/playground.html +3 -3
  83. package/dist/docs/reference/project-layout.html +3 -3
  84. package/dist/docs/reference/prompt.html +3 -3
  85. package/dist/docs/reference/schedules.html +3 -3
  86. package/dist/docs/reference/sessions.html +3 -3
  87. package/dist/docs/reference/skills.html +3 -3
  88. package/dist/docs/reference/subagents.html +3 -3
  89. package/dist/docs/reference/tools.html +3 -3
  90. package/dist/docs/scaffolding-agents.html +3 -3
  91. package/dist/docs/storage.html +3 -3
  92. package/dist/docs/templates/agentic-owners.html +3 -3
  93. package/dist/docs/templates/demo.html +3 -3
  94. package/dist/docs/templates/pr-autofixer.html +3 -3
  95. package/dist/docs/templates/security-reviewer.html +3 -3
  96. package/dist/docs/templates/triage.html +3 -3
  97. package/dist/docs/troubleshooting.html +3 -3
  98. package/dist/internal/authored-alias-hooks.d.ts +12 -0
  99. package/dist/internal/authored-alias-hooks.d.ts.map +1 -1
  100. package/dist/internal/authored-alias-hooks.js +60 -8
  101. package/dist/internal/authored-loaders.d.ts +5 -3
  102. package/dist/internal/authored-loaders.d.ts.map +1 -1
  103. package/dist/internal/authored-loaders.js +13 -4
  104. package/dist/internal/cursor-deployment-relay.d.ts +77 -0
  105. package/dist/internal/cursor-deployment-relay.d.ts.map +1 -0
  106. package/dist/internal/cursor-deployment-relay.js +201 -0
  107. package/dist/internal/deployment-scm-event-stream.js +4 -4
  108. package/dist/internal/distribution.d.ts.map +1 -1
  109. package/dist/internal/distribution.js +1 -0
  110. package/dist/internal/review-comments.d.ts +62 -0
  111. package/dist/internal/review-comments.d.ts.map +1 -1
  112. package/dist/internal/review-comments.js +199 -0
  113. package/dist/internal/server.d.ts.map +1 -1
  114. package/dist/internal/server.js +114 -5
  115. package/dist/playground/assets/{index-D_8I9TM0.js → index-BWqjJcRo.js} +1 -1
  116. package/dist/playground/assets/index-Bfwz2yLN.css +1 -0
  117. package/dist/playground/index.html +2 -2
  118. package/docs/reference/channels.md +15 -0
  119. package/package.json +7 -1
  120. package/src/channels/checks.ts +185 -0
  121. package/src/channels/deployments/deployments-channel.ts +109 -0
  122. package/src/channels/deployments/index.ts +26 -0
  123. package/src/channels/deployments/types.ts +80 -0
  124. package/src/channels/github/checks.ts +90 -115
  125. package/src/channels/origin/api.ts +318 -0
  126. package/src/channels/origin/binding.ts +96 -0
  127. package/src/channels/origin/checks.ts +250 -0
  128. package/src/channels/origin/index.ts +3 -0
  129. package/src/channels/origin/origin-channel.ts +18 -6
  130. package/src/channels/origin/types.ts +37 -0
  131. package/src/channels.ts +16 -0
  132. package/src/internal/authored-alias-hooks.ts +75 -7
  133. package/src/internal/authored-loaders.ts +15 -4
  134. package/src/internal/cursor-deployment-relay.ts +274 -0
  135. package/src/internal/deployment-scm-event-stream.ts +4 -4
  136. package/src/internal/distribution.ts +1 -0
  137. package/src/internal/review-comments.ts +286 -0
  138. package/src/internal/server.ts +149 -0
  139. package/dist/docs/assets/chunks/@localSearchIndexroot.DM5INS71.js +0 -1
  140. package/dist/playground/assets/index-M3nSMc6_.css +0 -1
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Origin CI checks — the Origin {@link CiCheckWriter} behind the shared check
3
+ * lifecycle in `../checks.js`.
4
+ *
5
+ * Origin check runs are idempotent upserts keyed by `(repo, head SHA, suite
6
+ * key, check key)`, so every write re-POSTs the full desired state with a
7
+ * fresh `externalUpdatedAt`, and `resumeOriginCheck` needs only those
8
+ * identity fields (no server id) to rebuild a handle after a restart.
9
+ *
10
+ * Posting needs `repository:checks:write` and is installation-only: the
11
+ * client must authenticate with an `oit_…` installation token (see
12
+ * `createOriginClient` / `originAppTokenProvider` in `api.js`). Works with
13
+ * any client exposing `request()` (`ctx.origin`, `createOriginClient()`),
14
+ * and is also reachable as `ctx.origin.createCheck(...)`. API failures throw
15
+ * `OriginApiError`.
16
+ */
17
+
18
+ import {
19
+ type CiCheck,
20
+ type CiCheckConcludeInput,
21
+ type CiCheckConclusion,
22
+ type CiCheckCreateInput,
23
+ type CiCheckOutput,
24
+ type CiCheckRef,
25
+ type CiCheckStatus,
26
+ type CiChecksClient,
27
+ type CiCheckUpdateInput,
28
+ type CiCheckWriter,
29
+ openCiCheck,
30
+ resumeCiCheck,
31
+ } from "../checks.js";
32
+ import type { OriginApiMethod } from "./api.js";
33
+
34
+ /** Any client that can call the Origin public REST API. */
35
+ export interface OriginCheckClient {
36
+ request<T = unknown>(input: {
37
+ method: OriginApiMethod;
38
+ path: string;
39
+ body?: unknown;
40
+ }): Promise<T>;
41
+ }
42
+
43
+ export type OriginCheckRunStatus = CiCheckStatus;
44
+
45
+ /** Origin accepts the shared conclusion set plus `stale`. */
46
+ export type OriginCheckRunConclusion = CiCheckConclusion | "stale";
47
+
48
+ export type OriginCheckOutput = CiCheckOutput;
49
+ export type OriginCheckUpdateInput =
50
+ CiCheckUpdateInput<OriginCheckRunConclusion>;
51
+ export type OriginCheckConcludeInput =
52
+ CiCheckConcludeInput<OriginCheckRunConclusion>;
53
+
54
+ /** A created check run. All methods throw `OriginApiError` on API failure. */
55
+ export type OriginCheck = CiCheck<OriginCheckRunConclusion> & CiCheckRef;
56
+
57
+ /** The upsert identity + client an Origin check is bound to. */
58
+ export interface OriginCheckIdentityInput {
59
+ /** Client used for every API call on the returned handle. */
60
+ origin: OriginCheckClient;
61
+ /** Repository owner slug, e.g. `"anysphere"`. */
62
+ owner: string;
63
+ /** Repository name, e.g. `"everysphere"`. */
64
+ repo: string;
65
+ /** Check name shown in the PR merge box, e.g. `"Cursor Security Review"`. */
66
+ name: string;
67
+ /** Commit the check attaches to (usually the PR head SHA). */
68
+ sha: string;
69
+ /**
70
+ * Stable, app-chosen key identifying the logical check across attempts
71
+ * (part of the upsert identity). Defaults to a slug of `name`.
72
+ */
73
+ key?: string;
74
+ /** Suite the check belongs to. Defaults to a suite mirroring the check. */
75
+ suite?: { key?: string; name?: string; detailsUrl?: string };
76
+ /**
77
+ * Provider-assigned immutable identity for this check attempt. Defaults to
78
+ * a deterministic id derived from the keys and `sha`.
79
+ */
80
+ externalId?: string;
81
+ }
82
+
83
+ export interface OriginCheckCreateInput
84
+ extends OriginCheckIdentityInput,
85
+ CiCheckCreateInput {}
86
+
87
+ /**
88
+ * Origin rejects the whole upsert when `output.title` exceeds 255 characters
89
+ * or a body exceeds 65,535 UTF-8 bytes, so the writer caps rather than
90
+ * risking a lost check write.
91
+ */
92
+ const OUTPUT_TITLE_MAX_CHARS = 255;
93
+ const OUTPUT_BODY_MAX_BYTES = 65_000;
94
+
95
+ /** Truncate to at most `maxBytes` UTF-8 bytes without severing a character. */
96
+ function capUtf8Bytes(text: string, maxBytes: number): string {
97
+ const encoded = new TextEncoder().encode(text);
98
+ if (encoded.length <= maxBytes) {
99
+ return text;
100
+ }
101
+ // A non-fatal decode turns a trailing partial character into U+FFFD;
102
+ // stripping it keeps the result valid (and re-encodable) under the cap.
103
+ return new TextDecoder("utf-8")
104
+ .decode(encoded.subarray(0, maxBytes))
105
+ .replace(/\uFFFD+$/, "");
106
+ }
107
+
108
+ function cappedOutput(
109
+ output: CiCheckOutput | undefined
110
+ ): Record<string, unknown> | undefined {
111
+ if (output === undefined) {
112
+ return undefined;
113
+ }
114
+ return {
115
+ title: output.title.slice(0, OUTPUT_TITLE_MAX_CHARS),
116
+ summary: capUtf8Bytes(output.summary, OUTPUT_BODY_MAX_BYTES),
117
+ ...(output.text === undefined
118
+ ? {}
119
+ : { text: capUtf8Bytes(output.text, OUTPUT_BODY_MAX_BYTES) }),
120
+ };
121
+ }
122
+
123
+ /** Derive a stable check key from a display name, e.g. `cursor-security-review`. */
124
+ export function originCheckKeyFromName(name: string): string {
125
+ const slug = name
126
+ .toLowerCase()
127
+ .replace(/[^a-z0-9]+/g, "-")
128
+ .replace(/^-+|-+$/g, "")
129
+ .slice(0, 100);
130
+ return slug === "" ? "check" : slug;
131
+ }
132
+
133
+ /**
134
+ * Origin writer: every write is a full keyed upsert. Tracks the last written
135
+ * status so an output-only update cannot regress a running check to queued.
136
+ */
137
+ function originCheckWriter(
138
+ input: OriginCheckIdentityInput & { status?: CiCheckStatus }
139
+ ): CiCheckWriter<OriginCheckRunConclusion> {
140
+ const key = input.key ?? originCheckKeyFromName(input.name);
141
+ const suiteKey = input.suite?.key ?? key;
142
+ const externalId =
143
+ input.externalId ?? `agent-serve:${suiteKey}:${key}:${input.sha}`;
144
+ const path = `/v1/origin/repos/${encodeURIComponent(input.owner)}/${encodeURIComponent(
145
+ input.repo
146
+ )}/check-runs`;
147
+ let lastStatus: CiCheckStatus = input.status ?? "in_progress";
148
+ return async (write) => {
149
+ const status =
150
+ write.status ??
151
+ (write.conclusion === undefined ? lastStatus : "completed");
152
+ lastStatus = status;
153
+ const response = await input.origin.request<{
154
+ checkRun?: { id?: unknown };
155
+ }>({
156
+ method: "POST",
157
+ path,
158
+ body: {
159
+ headSha: input.sha,
160
+ checkSuite: {
161
+ key: suiteKey,
162
+ name: input.suite?.name ?? input.name,
163
+ externalId: `agent-serve:${suiteKey}`,
164
+ ...(input.suite?.detailsUrl === undefined
165
+ ? {}
166
+ : { detailsUrl: input.suite.detailsUrl }),
167
+ },
168
+ checkRun: {
169
+ key,
170
+ name: input.name,
171
+ status,
172
+ conclusion: write.conclusion,
173
+ // Origin only applies an upsert when this timestamp is at least as
174
+ // new as the stored check; timestamp every write when it is built.
175
+ externalUpdatedAt: new Date().toISOString(),
176
+ externalId,
177
+ startedAt: write.startedAt,
178
+ completedAt: write.completedAt,
179
+ detailsUrl: write.detailsUrl,
180
+ output: cappedOutput(write.output),
181
+ },
182
+ },
183
+ });
184
+ const id = response?.checkRun?.id;
185
+ return typeof id === "string" ? { id } : undefined;
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Create a CI check run on a commit and return a handle for later updates.
191
+ *
192
+ * ```ts
193
+ * const check = await createOriginCheck({
194
+ * origin: ctx.origin,
195
+ * owner: ctx.repository.owner,
196
+ * repo: ctx.repository.name,
197
+ * name: "Cursor Security Review",
198
+ * sha: pr.headSha,
199
+ * detailsUrl: sessionUrl,
200
+ * output: { title: "Running…", summary: `[View session](${sessionUrl})` },
201
+ * });
202
+ * // …do the work…
203
+ * await check.conclude({ conclusion: "success", output: { … } });
204
+ * ```
205
+ */
206
+ export function createOriginCheck(
207
+ input: OriginCheckCreateInput
208
+ ): Promise<OriginCheck> {
209
+ return openCiCheck(originCheckWriter(input), input);
210
+ }
211
+
212
+ /**
213
+ * Rebuild an {@link OriginCheck} handle for an existing check run (e.g.
214
+ * after a process restart) from its upsert identity — no server id needed.
215
+ * No API call is made until the first update.
216
+ */
217
+ export function resumeOriginCheck(
218
+ input: OriginCheckIdentityInput & {
219
+ /** Last known status; defaults to `"in_progress"`. */
220
+ status?: CiCheckStatus;
221
+ /** Server-assigned id (`cr_…`) when known. */
222
+ id?: string;
223
+ }
224
+ ): OriginCheck {
225
+ return resumeCiCheck(
226
+ originCheckWriter(input),
227
+ input.id === undefined ? undefined : { id: input.id }
228
+ );
229
+ }
230
+
231
+ /**
232
+ * Repo-bound checks adapter satisfying the shared {@link CiChecksClient}
233
+ * contract — hand it to forge-agnostic check code when there is no channel
234
+ * `ctx.origin` (e.g. CI entrypoints).
235
+ */
236
+ export function bindOriginChecks(input: {
237
+ origin: OriginCheckClient;
238
+ owner: string;
239
+ repo: string;
240
+ }): CiChecksClient<OriginCheckRunConclusion> {
241
+ return {
242
+ createCheck: (check) =>
243
+ createOriginCheck({
244
+ ...check,
245
+ origin: input.origin,
246
+ owner: input.owner,
247
+ repo: input.repo,
248
+ }),
249
+ };
250
+ }
@@ -22,6 +22,9 @@
22
22
  * matching GitHub's `github-webhook` pattern.
23
23
  */
24
24
 
25
+ export * from "./api.js";
26
+ export * from "./binding.js";
27
+ export * from "./checks.js";
25
28
  export * from "./cursor-account.js";
26
29
  export * from "./events.js";
27
30
  export * from "./origin-channel.js";
@@ -13,6 +13,7 @@ import type {
13
13
  ChannelDefinition,
14
14
  ChannelHandlerArgs,
15
15
  } from "../../types.js";
16
+ import { buildOriginBinding } from "./binding.js";
16
17
  import {
17
18
  parseCursorOriginRepoScope,
18
19
  registerOriginCursorAccountRuntime,
@@ -268,7 +269,7 @@ function originSender(
268
269
  export function parseOriginPullRequestEvent(input: {
269
270
  envelope: OriginWebhookEnvelope;
270
271
  }): {
271
- ctx: Omit<OriginEventContext, "host" | "artifacts">;
272
+ ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">;
272
273
  pullRequest: OriginPullRequestEvent;
273
274
  payload: Record<string, unknown>;
274
275
  } | null {
@@ -294,6 +295,7 @@ export function parseOriginPullRequestEvent(input: {
294
295
  ctx: {
295
296
  deliveryId: input.envelope.deliveryId,
296
297
  eventType,
298
+ installationId: input.envelope.installationId,
297
299
  repository,
298
300
  pullRequestNumber: number,
299
301
  pullRequestUrl: url,
@@ -332,7 +334,7 @@ export function parseOriginPullRequestEvent(input: {
332
334
  * as {@link defaultGitHubAuth}).
333
335
  */
334
336
  export function defaultOriginAuth(
335
- ctx: Omit<OriginEventContext, "host" | "artifacts">
337
+ ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">
336
338
  ): AuthContext {
337
339
  const senderId = ctx.sender?.id;
338
340
  return {
@@ -363,7 +365,7 @@ export function defaultOriginTitle(
363
365
  }
364
366
 
365
367
  export function defaultOriginPullRequestMessage(
366
- ctx: Omit<OriginEventContext, "host" | "artifacts">,
368
+ ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">,
367
369
  pullRequest: OriginPullRequestEvent
368
370
  ): string {
369
371
  const head =
@@ -381,7 +383,7 @@ export function defaultOriginPullRequestMessage(
381
383
  }
382
384
 
383
385
  export function defaultOriginPullRequestDispatch(
384
- ctx: Omit<OriginEventContext, "host" | "artifacts">,
386
+ ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">,
385
387
  pullRequest: OriginPullRequestEvent
386
388
  ): OriginTurnDispatch | null {
387
389
  if (!DEFAULT_PULL_REQUEST_TYPES.has(pullRequest.type)) {
@@ -397,13 +399,19 @@ function isTaskDispatch(
397
399
  }
398
400
 
399
401
  function enrichEventContext(input: {
400
- ctx: Omit<OriginEventContext, "host" | "artifacts">;
402
+ ctx: Omit<OriginEventContext, "host" | "artifacts" | "origin">;
401
403
  args: ChannelHandlerArgs;
404
+ options: OriginChannelOptions;
402
405
  }): OriginEventContext {
403
406
  return {
404
407
  ...input.ctx,
405
408
  host: input.args.host,
406
409
  artifacts: input.args.artifacts,
410
+ origin: buildOriginBinding({
411
+ repository: input.ctx.repository,
412
+ installationId: input.ctx.installationId,
413
+ app: input.options.app,
414
+ }),
407
415
  };
408
416
  }
409
417
 
@@ -525,7 +533,11 @@ export async function dispatchOriginWebhook(input: {
525
533
  { status: 400 }
526
534
  );
527
535
  }
528
- const ctx = enrichEventContext({ ctx: parsed.ctx, args: input.args });
536
+ const ctx = enrichEventContext({
537
+ ctx: parsed.ctx,
538
+ args: input.args,
539
+ options: input.options,
540
+ });
529
541
  const { pullRequest, payload } = parsed;
530
542
  const repoAllowlist = configuredOriginRepoAllowlist(input.options);
531
543
  if (
@@ -6,6 +6,8 @@ import type {
6
6
  ChannelStartArgs,
7
7
  HostContext,
8
8
  } from "../../types.js";
9
+ import type { OriginTokenProvider } from "./api.js";
10
+ import type { OriginHandle } from "./binding.js";
9
11
 
10
12
  export interface OriginActor {
11
13
  id: string;
@@ -48,12 +50,21 @@ export interface OriginReview {
48
50
  export interface OriginEventContext {
49
51
  deliveryId?: string;
50
52
  eventType: string;
53
+ /** Origin app installation id from the delivery envelope, when present. */
54
+ installationId?: string;
51
55
  repository: OriginRepositoryRef;
52
56
  pullRequestNumber: number;
53
57
  pullRequestUrl: string;
54
58
  sender?: OriginActor;
55
59
  host: HostContext;
56
60
  artifacts: ArtifactsApi;
61
+ /**
62
+ * Repo-bound Origin public API handle (REST calls + `createCheck`).
63
+ * Outbound credentials come from {@link OriginChannelOptions.app} or the
64
+ * `ORIGIN_APP_*` environment; resolution is lazy, so hooks that never call
65
+ * it need no credentials.
66
+ */
67
+ origin: OriginHandle;
57
68
  }
58
69
 
59
70
  export type OriginTurnDispatch = {
@@ -114,6 +125,26 @@ export interface OriginWebhookEnvelope {
114
125
  };
115
126
  }
116
127
 
128
+ /**
129
+ * Outbound Origin app credentials for `ctx.origin` (public REST calls, CI
130
+ * check runs). Fields left unset fall back to the environment
131
+ * (`ORIGIN_APP_ID` / `ORIGIN_APP_INSTALLATION_ID` / `ORIGIN_APP_PRIVATE_KEY`
132
+ * / `ORIGIN_API_BASE_URL`); the delivery's installation id fills in when
133
+ * neither pins one.
134
+ */
135
+ export interface OriginAppOptions {
136
+ /** Origin app id (`app_<typeid>`). */
137
+ appId?: string;
138
+ /** Installation id outbound tokens are minted for. */
139
+ installationId?: string;
140
+ /** PKCS8 PEM for the app's EdDSA (Ed25519) private key. */
141
+ privateKey?: string;
142
+ /** Static bearer (`oit_…`) or provider, bypassing the app-JWT mint. */
143
+ token?: string | OriginTokenProvider;
144
+ /** Public Origin REST base, e.g. `https://api.cursor.com`. */
145
+ apiBaseUrl?: string;
146
+ }
147
+
117
148
  export interface OriginChannelOptions {
118
149
  /**
119
150
  * Use the signed-in Cursor account for inbound Origin events via
@@ -149,6 +180,12 @@ export interface OriginChannelOptions {
149
180
  * (GitHub's no-secret posture); `--dev` / skip-verify admits loopback only.
150
181
  */
151
182
  auth?: AuthPolicy[];
183
+ /**
184
+ * Outbound Origin app credentials for `ctx.origin` (REST calls, CI check
185
+ * runs). Optional: every field falls back to the `ORIGIN_APP_*`
186
+ * environment, and inbound-only channels need none.
187
+ */
188
+ app?: OriginAppOptions;
152
189
  /** Route path under `/v1/channels/<name>`. Defaults to `/`. */
153
190
  path?: string;
154
191
  /** Origin API base used to fetch `/v1/origin/keys` for v1ed verify. */
package/src/channels.ts CHANGED
@@ -47,6 +47,22 @@
47
47
  * Handlers receive `args.body` / `args.query` inferred from those schemas.
48
48
  */
49
49
 
50
+ export {
51
+ type CiCheck,
52
+ type CiCheckConcludeInput,
53
+ type CiCheckConclusion,
54
+ type CiCheckCreateInput,
55
+ type CiCheckOutput,
56
+ type CiCheckRef,
57
+ type CiCheckStatus,
58
+ type CiChecksClient,
59
+ type CiCheckUpdateInput,
60
+ type CiCheckWrite,
61
+ type CiCheckWriter,
62
+ openCiCheck,
63
+ resumeCiCheck,
64
+ } from "./channels/checks.js";
65
+
50
66
  import { Buffer } from "node:buffer";
51
67
  import { timingSafeEqual } from "node:crypto";
52
68
  import { brandDefinition } from "./internal/brand.js";
@@ -14,16 +14,29 @@
14
14
  * project's own node_modules; `fallbackAliases` only apply when that
15
15
  * fails (npx/global installs, projects without a local dependency), so a
16
16
  * real install in the user's project keeps precedence.
17
+ *
18
+ * A third case is first-party factory agents that relative-import this
19
+ * package's `src/` from a monorepo checkout. Hosted engines `npm install`
20
+ * only the agent directory, so files under `packages/agent-serve/src` have
21
+ * no `node_modules`. Bare specifiers from that tree (`zod`, octokit, …)
22
+ * retry against the running `@cursor/july` package.json, which does.
17
23
  */
18
24
 
19
25
  // Null-prototype copies so specifiers like "constructor" or "__proto__"
20
26
  // can never resolve to inherited Object.prototype members.
21
27
  let directAliases = toNullProtoMap({});
22
28
  let fallbackAliases = toNullProtoMap({});
29
+ let sourceTreeFallbackParentURL: string | undefined;
23
30
 
24
31
  export interface AuthoredAliasHookData {
25
32
  directAliases: Record<string, string>;
26
33
  fallbackAliases: Record<string, string>;
34
+ /**
35
+ * `file:` URL of the running `@cursor/july` package.json. Bare imports
36
+ * from monorepo `packages/agent-serve/` source retry against this parent
37
+ * when the source tree has no `node_modules` (hosted factory engines).
38
+ */
39
+ sourceTreeFallbackParentURL?: string;
27
40
  }
28
41
 
29
42
  function toNullProtoMap(
@@ -35,6 +48,9 @@ function toNullProtoMap(
35
48
  export function initialize(data: AuthoredAliasHookData): void {
36
49
  directAliases = toNullProtoMap(data.directAliases);
37
50
  fallbackAliases = toNullProtoMap(data.fallbackAliases);
51
+ const parent = data.sourceTreeFallbackParentURL;
52
+ sourceTreeFallbackParentURL =
53
+ typeof parent === "string" && parent !== "" ? parent : undefined;
38
54
  }
39
55
 
40
56
  interface ResolveResult {
@@ -58,12 +74,64 @@ export function resolve(
58
74
  return { url: direct, shortCircuit: true };
59
75
  }
60
76
  const fallback = fallbackAliases[specifier];
61
- if (fallback === undefined) {
62
- // Not ours: pass through with no promise/try-catch overhead. This hook
63
- // runs on every module resolution in the process.
64
- return nextResolve(specifier, context);
77
+ if (fallback !== undefined) {
78
+ return Promise.resolve()
79
+ .then(() => nextResolve(specifier, context))
80
+ .catch(() => ({ url: fallback, shortCircuit: true }));
81
+ }
82
+ if (
83
+ sourceTreeFallbackParentURL !== undefined &&
84
+ isBarePackageSpecifier(specifier) &&
85
+ isMonorepoAgentServeSource(parentURLOf(context))
86
+ ) {
87
+ const retryParent = sourceTreeFallbackParentURL;
88
+ return Promise.resolve()
89
+ .then(() => nextResolve(specifier, context))
90
+ .catch(() => nextResolve(specifier, withParentURL(context, retryParent)));
91
+ }
92
+ // Not ours: pass through with no promise/try-catch overhead. This hook
93
+ // runs on every module resolution in the process.
94
+ return nextResolve(specifier, context);
95
+ }
96
+
97
+ function parentURLOf(context: unknown): string | undefined {
98
+ if (context === null || typeof context !== "object") {
99
+ return undefined;
100
+ }
101
+ const parentURL = (context as { parentURL?: unknown }).parentURL;
102
+ return typeof parentURL === "string" ? parentURL : undefined;
103
+ }
104
+
105
+ function withParentURL(context: unknown, parentURL: string): unknown {
106
+ if (context !== null && typeof context === "object") {
107
+ return { ...(context as Record<string, unknown>), parentURL };
108
+ }
109
+ return { parentURL };
110
+ }
111
+
112
+ function isBarePackageSpecifier(specifier: string): boolean {
113
+ if (specifier.length === 0) {
114
+ return false;
115
+ }
116
+ const first = specifier[0];
117
+ if (first === "." || first === "/" || first === "\\") {
118
+ return false;
119
+ }
120
+ return !(
121
+ specifier.startsWith("node:") ||
122
+ specifier.startsWith("file:") ||
123
+ specifier.startsWith("data:") ||
124
+ specifier.startsWith("http:") ||
125
+ specifier.startsWith("https:")
126
+ );
127
+ }
128
+
129
+ function isMonorepoAgentServeSource(parentURL: string | undefined): boolean {
130
+ if (parentURL === undefined) {
131
+ return false;
65
132
  }
66
- return Promise.resolve()
67
- .then(() => nextResolve(specifier, context))
68
- .catch(() => ({ url: fallback, shortCircuit: true }));
133
+ return (
134
+ parentURL.includes("/packages/agent-serve/") &&
135
+ !parentURL.includes("/node_modules/")
136
+ );
69
137
  }
@@ -11,9 +11,11 @@
11
11
  * imports of the framework resolve against the running package: the
12
12
  * historical `@anysphere/agent-serve[/x]` name always, and
13
13
  * `@cursor/july[/x]` / `@cursor/agentkit[/x]` as fallbacks when the user's
14
- * project has no local install (npx / global). Source runs (tsx from the
15
- * monorepo) skip both the host loader and tsconfig paths already cover
16
- * them.
14
+ * project has no local install (npx / global). The same hooks retry bare
15
+ * npm specifiers from monorepo `packages/agent-serve/` source against this
16
+ * package (hosted factory engines install only the agent directory).
17
+ * Source runs (tsx from the monorepo) skip both — the host loader and
18
+ * tsconfig paths already cover them.
17
19
  */
18
20
 
19
21
  import { stat } from "node:fs/promises";
@@ -175,7 +177,16 @@ function registerFrameworkAliases(): void {
175
177
  fallbackAliases[`${AGENTKIT_PACKAGE_NAME}${suffix}`] = target;
176
178
  }
177
179
  const hooksUrl = new URL("./authored-alias-hooks.js", import.meta.url);
178
- const data: AuthoredAliasHookData = { directAliases, fallbackAliases };
180
+ const data: AuthoredAliasHookData = {
181
+ directAliases,
182
+ fallbackAliases,
183
+ // Hosted factory agents relative-import this package's src/ from a
184
+ // monorepo checkout while npm install ran only in the agent directory.
185
+ // Bare specifiers from that tree retry against this package.json.
186
+ sourceTreeFallbackParentURL: pathToFileURL(
187
+ join(packageRootDir(), "package.json")
188
+ ).href,
189
+ };
179
190
  // `node:module`'s register() is synchronous: it blocks until the hooks
180
191
  // thread has initialized, so the aliases are active when it returns.
181
192
  registerModuleHooks(hooksUrl, { data });