@cursor/july 0.1.79 → 0.1.82

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 (93) hide show
  1. package/AGENTS.md +0 -12
  2. package/dist/channels/github/api.d.ts.map +1 -1
  3. package/dist/channels/github/api.js +8 -1
  4. package/dist/channels/github/cursor-account.d.ts +28 -0
  5. package/dist/channels/github/cursor-account.d.ts.map +1 -1
  6. package/dist/channels/github/cursor-account.js +60 -0
  7. package/dist/channels/github/github-channel.d.ts.map +1 -1
  8. package/dist/channels/github/github-channel.js +29 -1
  9. package/dist/channels/github/types.d.ts +8 -0
  10. package/dist/channels/github/types.d.ts.map +1 -1
  11. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  12. package/dist/channels/origin/origin-channel.js +32 -10
  13. package/dist/channels/origin/types.d.ts +7 -0
  14. package/dist/channels/origin/types.d.ts.map +1 -1
  15. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  16. package/dist/channels/slack/cursor-account.js +2 -2
  17. package/dist/channels/slack/dispatch.d.ts +3 -0
  18. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  19. package/dist/channels/slack/dispatch.js +18 -18
  20. package/dist/channels/slack/interactive.d.ts +2 -0
  21. package/dist/channels/slack/interactive.d.ts.map +1 -1
  22. package/dist/channels/slack/interactive.js +4 -16
  23. package/dist/channels/slack/slack-channel.d.ts +1 -1
  24. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  25. package/dist/channels/slack/slack-channel.js +21 -10
  26. package/dist/channels/slack/types.d.ts +7 -0
  27. package/dist/channels/slack/types.d.ts.map +1 -1
  28. package/dist/channels.d.ts.map +1 -1
  29. package/dist/channels.js +4 -0
  30. package/dist/continuation.d.ts +20 -0
  31. package/dist/continuation.d.ts.map +1 -0
  32. package/dist/continuation.js +18 -0
  33. package/dist/files.d.ts +9 -3
  34. package/dist/files.d.ts.map +1 -1
  35. package/dist/files.js +9 -3
  36. package/dist/index.d.ts +5 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +4 -1
  39. package/dist/internal/continuation-channel.d.ts +29 -0
  40. package/dist/internal/continuation-channel.d.ts.map +1 -0
  41. package/dist/internal/continuation-channel.js +88 -0
  42. package/dist/internal/continuation-identity.d.ts +95 -0
  43. package/dist/internal/continuation-identity.d.ts.map +1 -0
  44. package/dist/internal/continuation-identity.js +445 -0
  45. package/dist/internal/deploy-manifest.d.ts +12 -2
  46. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  47. package/dist/internal/deploy-manifest.js +15 -7
  48. package/dist/internal/discovery.d.ts.map +1 -1
  49. package/dist/internal/discovery.js +32 -5
  50. package/dist/internal/review-comments.d.ts +37 -7
  51. package/dist/internal/review-comments.d.ts.map +1 -1
  52. package/dist/internal/review-comments.js +209 -10
  53. package/dist/internal/server.d.ts.map +1 -1
  54. package/dist/internal/server.js +117 -55
  55. package/dist/internal/session-engine.d.ts +24 -0
  56. package/dist/internal/session-engine.d.ts.map +1 -1
  57. package/dist/internal/session-engine.js +104 -34
  58. package/dist/internal/session-store.d.ts.map +1 -1
  59. package/dist/internal/session-store.js +6 -0
  60. package/dist/internal/skills-sync.d.ts +25 -0
  61. package/dist/internal/skills-sync.d.ts.map +1 -0
  62. package/dist/internal/skills-sync.js +96 -0
  63. package/dist/playground/assets/{index-DJzcIeGt.css → index-ChETdG-_.css} +1 -1
  64. package/dist/playground/assets/{index-K3Sxyx0I.js → index-CkuIrhg4.js} +3 -3
  65. package/dist/playground/index.html +2 -2
  66. package/dist/types.d.ts +44 -0
  67. package/dist/types.d.ts.map +1 -1
  68. package/package.json +1 -1
  69. package/src/channels/github/api.ts +8 -0
  70. package/src/channels/github/cursor-account.ts +76 -0
  71. package/src/channels/github/github-channel.ts +52 -1
  72. package/src/channels/github/types.ts +8 -0
  73. package/src/channels/origin/origin-channel.ts +48 -10
  74. package/src/channels/origin/types.ts +7 -0
  75. package/src/channels/slack/cursor-account.ts +1 -0
  76. package/src/channels/slack/dispatch.ts +44 -2
  77. package/src/channels/slack/interactive.ts +5 -0
  78. package/src/channels/slack/slack-channel.ts +23 -2
  79. package/src/channels/slack/types.ts +7 -0
  80. package/src/channels.ts +7 -0
  81. package/src/continuation.ts +49 -0
  82. package/src/files.ts +10 -3
  83. package/src/index.ts +30 -1
  84. package/src/internal/continuation-channel.ts +129 -0
  85. package/src/internal/continuation-identity.ts +597 -0
  86. package/src/internal/deploy-manifest.ts +36 -3
  87. package/src/internal/discovery.ts +40 -2
  88. package/src/internal/review-comments.ts +257 -16
  89. package/src/internal/server.ts +146 -68
  90. package/src/internal/session-engine.ts +132 -37
  91. package/src/internal/session-store.ts +6 -0
  92. package/src/internal/skills-sync.ts +107 -0
  93. package/src/types.ts +55 -0
@@ -23,6 +23,7 @@ import { httpChannel } from "../channels.js";
23
23
  import type { OtelDefinition } from "../otel.js";
24
24
  import type { StorageDefinition } from "../storage.js";
25
25
  import {
26
+ type AgentArchitecture,
26
27
  type AgentConcurrencyOptions,
27
28
  type AgentConfigDefinition,
28
29
  type AgentHostingOptions,
@@ -68,6 +69,7 @@ import {
68
69
  builtinTools,
69
70
  TAG_ARTIFACT_TOOL_NAME,
70
71
  } from "./builtin-tools/index.js";
72
+ import { applyAgentContinuationDefaults } from "./continuation-channel.js";
71
73
  import { isValidCron } from "./cron.js";
72
74
  import { parseFrontmatter } from "./frontmatter.js";
73
75
  import {
@@ -311,12 +313,19 @@ export async function loadAgentProject(
311
313
  await warnUnknownSlots(ctx, agentDir);
312
314
  }
313
315
 
316
+ const resolvedHttpChannel = httpChannelDefinition ?? httpChannel();
317
+ applyAgentContinuationDefaults({
318
+ agent,
319
+ channels,
320
+ httpChannel: resolvedHttpChannel,
321
+ });
322
+
314
323
  return {
315
324
  rootDir,
316
325
  agentDir,
317
326
  name: agent.name,
318
327
  agent,
319
- httpChannel: httpChannelDefinition ?? httpChannel(),
328
+ httpChannel: resolvedHttpChannel,
320
329
  channels,
321
330
  schedules,
322
331
  hooks,
@@ -387,6 +396,13 @@ async function loadAgent(
387
396
  "tools is not supported on subagents (they keep the harness task-tool toolset); ignoring.",
388
397
  });
389
398
  }
399
+ if (config?.architecture !== undefined) {
400
+ ctx.diagnostics.push({
401
+ severity: "warning",
402
+ path: displayPath(ctx, join(agentDir, "agent.ts")),
403
+ message: "architecture is not supported on subagents; ignoring.",
404
+ });
405
+ }
390
406
  const artifactsPath = await findModuleFile(agentDir, "artifacts");
391
407
  if (artifactsPath !== undefined) {
392
408
  ctx.diagnostics.push({
@@ -505,6 +521,7 @@ async function loadAgent(
505
521
  };
506
522
  const hosting = resolveHosting(ctx, agentDir, config?.hosting);
507
523
  const concurrency = resolveConcurrency(ctx, agentDir, config?.concurrency);
524
+ const architecture = resolveArchitecture(ctx, agentDir, config?.architecture);
508
525
 
509
526
  if (runtime === "cloud") {
510
527
  warnCloudRuntimeLimitations(ctx, agentDir, {
@@ -529,6 +546,7 @@ async function loadAgent(
529
546
  ...(allowedTools === undefined ? {} : { allowedTools }),
530
547
  hosting,
531
548
  concurrency,
549
+ ...(architecture === undefined ? {} : { architecture }),
532
550
  tools,
533
551
  skills,
534
552
  connections,
@@ -537,6 +555,26 @@ async function loadAgent(
537
555
  };
538
556
  }
539
557
 
558
+ function resolveArchitecture(
559
+ ctx: DiscoveryContext,
560
+ agentDir: string,
561
+ architecture: AgentArchitecture | undefined
562
+ ): AgentArchitecture | undefined {
563
+ if (
564
+ architecture === undefined ||
565
+ architecture === "v1" ||
566
+ architecture === "v2"
567
+ ) {
568
+ return architecture;
569
+ }
570
+ ctx.diagnostics.push({
571
+ severity: "error",
572
+ path: displayPath(ctx, join(agentDir, "agent.ts")),
573
+ message: `architecture must be "v1" or "v2"; got ${JSON.stringify(architecture)}.`,
574
+ });
575
+ return undefined;
576
+ }
577
+
540
578
  /** Absolute harness cwd parent, or undefined when unset / blank. */
541
579
  function resolveAgentCwd(
542
580
  ctx: DiscoveryContext,
@@ -1549,7 +1587,7 @@ function warnCloudRuntimeLimitations(
1549
1587
  severity: "warning",
1550
1588
  path: displayPath(ctx, join(agentDir, "skills")),
1551
1589
  message:
1552
- "Authored skills are materialized into the local session workspace only; cloud agents see skills that already exist in the cloud repo.",
1590
+ "Authored skills reach cloud agents on hosted deployments via the deployment store's skills/ directory (native skill discovery). Cloud turns started outside hosting (dev serve/run) see only skills already in the cloud repo.",
1553
1591
  });
1554
1592
  }
1555
1593
  if (counts.seedFiles > 0) {
@@ -1,7 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import type { HostGitHubClient } from "../types.js";
3
3
 
4
- export type ReviewCommentPublisher = "local_gh" | "host_github";
4
+ export type ReviewCommentPublisher = "local_gh" | "host_github" | "host_origin";
5
5
 
6
6
  export type ReviewCommentPostResult = {
7
7
  posted: boolean;
@@ -57,12 +57,25 @@ export type ReviewCommentPublisherImplementation = {
57
57
  };
58
58
 
59
59
  /**
60
- * SCMs this module can read review comments back from. Same growth seam as
61
- * `shallow-clone.ts`'s {@link import("./shallow-clone.js").ScmProvider}:
62
- * adding GitLab/Bitbucket/Origin means a resolver case plus a reader
63
- * implementation, and nothing above this line changes.
60
+ * SCMs this module can address. Same growth seam as `shallow-clone.ts`'s
61
+ * {@link import("./shallow-clone.js").ScmProvider}: adding a forge means a
62
+ * resolver case plus a publisher (and, when the public API supports it, a
63
+ * reader / thread resolver). Callers above this line stay provider-neutral.
64
64
  */
65
- export type ScmReviewProvider = "github";
65
+ export type ScmReviewProvider = "github" | "origin";
66
+
67
+ /**
68
+ * Origin public REST client used to post PR reviews (`ctx.origin` /
69
+ * {@link import("../channels/origin/api.js").createOriginClient}). Narrower
70
+ * than the full Origin handle so tests and CLI paths can inject `request()`.
71
+ */
72
+ export type HostOriginReviewClient = {
73
+ request(input: {
74
+ method: "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
75
+ path: string;
76
+ body?: unknown;
77
+ }): Promise<unknown>;
78
+ };
66
79
 
67
80
  /** Which SCM's PR to read review comments from. */
68
81
  export type ScmReviewTarget = {
@@ -147,18 +160,12 @@ export type ReviewThreadResolverImplementation = {
147
160
  ) => Promise<ResolveReviewThreadsResult>;
148
161
  };
149
162
 
150
- /** Parse a PR URL into the SCM target to read comments from. */
163
+ /** Parse a PR URL into the SCM target to read or post comments on. */
151
164
  export function resolveScmReviewTarget(
152
165
  prUrl: string
153
166
  ): ScmReviewTarget | undefined {
154
- const parsed = parseGithubPrUrl(prUrl);
155
- return parsed.kind === "valid"
156
- ? {
157
- provider: "github",
158
- repo: parsed.value.repo,
159
- prNumber: parsed.value.prNumber,
160
- }
161
- : undefined;
167
+ const parsed = parseReviewPrUrl(prUrl);
168
+ return parsed.kind === "valid" ? parsed.value : undefined;
162
169
  }
163
170
 
164
171
  /**
@@ -175,6 +182,10 @@ export function createReviewCommentReader(args: {
175
182
  return args.github === undefined
176
183
  ? undefined
177
184
  : createHostGithubReviewCommentReader(args.github);
185
+ case "origin":
186
+ // Public Origin REST lists discussion comments, not diff-anchored
187
+ // review threads. Stay undefined until a reader can fetch by id.
188
+ return undefined;
178
189
  }
179
190
  }
180
191
 
@@ -191,6 +202,8 @@ export function createReviewThreadResolver(args: {
191
202
  return args.github === undefined
192
203
  ? undefined
193
204
  : createHostGithubReviewThreadResolver(args.github);
205
+ case "origin":
206
+ return undefined;
194
207
  }
195
208
  }
196
209
 
@@ -222,7 +235,11 @@ export async function resolveReviewThreadsForComments(args: {
222
235
  github: args.github,
223
236
  });
224
237
  if (resolver === undefined) {
225
- return skippedResolve("no GitHub client bound to resolve threads");
238
+ return skippedResolve(
239
+ target.provider === "github"
240
+ ? "no GitHub client bound to resolve threads"
241
+ : `no thread resolver for ${target.provider}`
242
+ );
226
243
  }
227
244
  return resolver.resolveReviewThreadsForComments({
228
245
  target,
@@ -560,6 +577,49 @@ export function createHostGithubReviewPublisher(
560
577
  };
561
578
  }
562
579
 
580
+ /**
581
+ * Pick the publisher for `prUrl`'s SCM. GitHub falls back to local `gh`
582
+ * when no host client is bound (CLI). Origin has no local CLI publisher —
583
+ * returns undefined unless an Origin REST client is provided.
584
+ */
585
+ export function createReviewCommentPublisher(args: {
586
+ prUrl: string;
587
+ github?: HostGitHubClient;
588
+ origin?: HostOriginReviewClient;
589
+ }): ReviewCommentPublisherImplementation | undefined {
590
+ const target = resolveScmReviewTarget(args.prUrl);
591
+ if (target === undefined) {
592
+ return undefined;
593
+ }
594
+ switch (target.provider) {
595
+ case "github":
596
+ return args.github !== undefined
597
+ ? createHostGithubReviewPublisher(args.github)
598
+ : createLocalGhReviewPublisher();
599
+ case "origin":
600
+ return args.origin === undefined
601
+ ? undefined
602
+ : createHostOriginReviewPublisher(args.origin);
603
+ }
604
+ }
605
+
606
+ /**
607
+ * Posts a COMMENT-verdict Origin review through the public REST API.
608
+ *
609
+ * Public Origin REST has no diff-anchored review comments (those are an
610
+ * internal CreateCommentThread RPC). Inline comments in the request are
611
+ * folded into the review body so findings still land on the PR.
612
+ */
613
+ export function createHostOriginReviewPublisher(
614
+ origin: HostOriginReviewClient
615
+ ): ReviewCommentPublisherImplementation {
616
+ return {
617
+ async postReview(args) {
618
+ return postReviewViaHostOrigin(origin, args);
619
+ },
620
+ };
621
+ }
622
+
563
623
  async function postReviewViaHostGithub(
564
624
  github: HostGitHubClient,
565
625
  args: ReviewCommentPostArgs
@@ -779,6 +839,76 @@ function skippedHost(detail: string): ReviewCommentPostResult {
779
839
  };
780
840
  }
781
841
 
842
+ function skippedOrigin(detail: string): ReviewCommentPostResult {
843
+ return {
844
+ posted: false,
845
+ publisher: "host_origin",
846
+ mode: "skipped",
847
+ inlineCommentCount: 0,
848
+ detail,
849
+ };
850
+ }
851
+
852
+ async function postReviewViaHostOrigin(
853
+ origin: HostOriginReviewClient,
854
+ args: ReviewCommentPostArgs
855
+ ): Promise<ReviewCommentPostResult> {
856
+ const target = resolveScmReviewTarget(args.prUrl);
857
+ if (target === undefined || target.provider !== "origin") {
858
+ return skippedOrigin("host Origin publisher only supports Origin PRs");
859
+ }
860
+ const [owner, repo] = target.repo.split("/", 2);
861
+ if (owner === undefined || repo === undefined || repo === "") {
862
+ return skippedOrigin("unparseable owner/repo from PR URL");
863
+ }
864
+
865
+ try {
866
+ args.signal?.throwIfAborted();
867
+ await origin.request({
868
+ method: "POST",
869
+ path: `/v1/origin/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${target.prNumber}/reviews`,
870
+ body: {
871
+ verdict: "comment",
872
+ body: originReviewBody(args),
873
+ },
874
+ });
875
+ return {
876
+ posted: true,
877
+ publisher: "host_origin",
878
+ mode: "review_body_only",
879
+ inlineCommentCount: 0,
880
+ detail: `posted Origin review on ${target.repo}#${target.prNumber}`,
881
+ };
882
+ } catch (error) {
883
+ if (args.signal?.aborted) {
884
+ throw error;
885
+ }
886
+ return skippedOrigin(
887
+ `host Origin review post failed: ${error instanceof Error ? error.message : String(error)}`
888
+ );
889
+ }
890
+ }
891
+
892
+ /**
893
+ * Origin's public CreatePullRequestReview has a body, not inline comments.
894
+ * Fold attempted anchors into the summary so findings are not dropped.
895
+ */
896
+ function originReviewBody(args: ReviewCommentPostArgs): string {
897
+ if (args.comments.length === 0) {
898
+ return args.message;
899
+ }
900
+ const sections = args.comments.map((comment) => {
901
+ const location =
902
+ comment.start_line !== undefined &&
903
+ comment.start_line > 0 &&
904
+ comment.start_line !== comment.line
905
+ ? `${comment.file}:${comment.start_line}-${comment.line}`
906
+ : `${comment.file}:${comment.line}`;
907
+ return `### \`${location}\`\n\n${comment.body}`;
908
+ });
909
+ return [args.message, ...sections].join("\n\n");
910
+ }
911
+
782
912
  function skippedResolve(detail: string): ResolveReviewThreadsResult {
783
913
  return {
784
914
  requested: 0,
@@ -790,6 +920,117 @@ function skippedResolve(detail: string): ResolveReviewThreadsResult {
790
920
  };
791
921
  }
792
922
 
923
+ const ORIGIN_PR_PAGE_HOST_PATTERN = /^origin(-[a-z0-9]+)?\.cursor\.com$/i;
924
+
925
+ /**
926
+ * Provider-neutral PR URL parse. GitHub.com plus the Origin page shapes
927
+ * `parsePrTarget` in security-review-lib accepts. Duplicated here so
928
+ * agent-serve does not import that package.
929
+ */
930
+ function parseReviewPrUrl(
931
+ prUrl: string
932
+ ):
933
+ | { kind: "valid"; value: ScmReviewTarget }
934
+ | { kind: "invalid"; detail: string } {
935
+ let url: URL;
936
+ try {
937
+ url = new URL(prUrl);
938
+ } catch (error) {
939
+ return {
940
+ kind: "invalid",
941
+ detail: `unparseable PR URL: ${error instanceof Error ? error.message : String(error)}`,
942
+ };
943
+ }
944
+
945
+ const parts = url.pathname.split("/").filter(Boolean);
946
+ const hostname = url.hostname;
947
+
948
+ if (hostname === "github.com" && parts.length >= 4 && parts[2] === "pull") {
949
+ const prNumber = Number(parts[3]);
950
+ if (Number.isInteger(prNumber) && prNumber > 0) {
951
+ return {
952
+ kind: "valid",
953
+ value: {
954
+ provider: "github",
955
+ repo: `${parts[0]}/${parts[1]}`,
956
+ prNumber,
957
+ },
958
+ };
959
+ }
960
+ }
961
+
962
+ if (
963
+ ORIGIN_PR_PAGE_HOST_PATTERN.test(hostname) &&
964
+ parts.length >= 4 &&
965
+ parts[2] === "github"
966
+ ) {
967
+ const prNumber = Number(parts[3]);
968
+ if (Number.isInteger(prNumber) && prNumber > 0) {
969
+ return {
970
+ kind: "valid",
971
+ value: {
972
+ provider: "origin",
973
+ repo: `${parts[0]}/${parts[1]}`,
974
+ prNumber,
975
+ },
976
+ };
977
+ }
978
+ }
979
+
980
+ if (
981
+ /^(www\.)?cursor\.com$/i.test(hostname) &&
982
+ parts[0] === "codebase" &&
983
+ parts.length >= 5 &&
984
+ parts[3] === "pull"
985
+ ) {
986
+ const prNumber = Number(parts[4]);
987
+ if (Number.isInteger(prNumber) && prNumber > 0) {
988
+ return {
989
+ kind: "valid",
990
+ value: {
991
+ provider: "origin",
992
+ repo: `${parts[1]}/${parts[2]}`,
993
+ prNumber,
994
+ },
995
+ };
996
+ }
997
+ }
998
+
999
+ if (hostname === "review.cursor.com" && parts[0] === "pr") {
1000
+ if (parts.length >= 5 && parts[3] === "github") {
1001
+ const prNumber = Number(parts[4]);
1002
+ if (Number.isInteger(prNumber) && prNumber > 0) {
1003
+ return {
1004
+ kind: "valid",
1005
+ value: {
1006
+ provider: "origin",
1007
+ repo: `${parts[1]}/${parts[2]}`,
1008
+ prNumber,
1009
+ },
1010
+ };
1011
+ }
1012
+ }
1013
+ if (parts.length >= 4) {
1014
+ const prNumber = Number(parts[3]);
1015
+ if (Number.isInteger(prNumber) && prNumber > 0) {
1016
+ return {
1017
+ kind: "valid",
1018
+ value: {
1019
+ provider: "origin",
1020
+ repo: `${parts[1]}/${parts[2]}`,
1021
+ prNumber,
1022
+ },
1023
+ };
1024
+ }
1025
+ }
1026
+ }
1027
+
1028
+ return {
1029
+ kind: "invalid",
1030
+ detail: "unparseable PR URL: not a GitHub or Origin pull request",
1031
+ };
1032
+ }
1033
+
793
1034
  function parseGithubPrUrl(
794
1035
  prUrl: string
795
1036
  ):