@cursor/july 0.1.80 → 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 (145) hide show
  1. package/AGENTS.md +0 -12
  2. package/dist/channels/github/cursor-account.d.ts +11 -0
  3. package/dist/channels/github/cursor-account.d.ts.map +1 -1
  4. package/dist/channels/github/cursor-account.js +13 -0
  5. package/dist/channels/github/github-channel.d.ts.map +1 -1
  6. package/dist/channels/github/github-channel.js +29 -1
  7. package/dist/channels/github/types.d.ts +8 -0
  8. package/dist/channels/github/types.d.ts.map +1 -1
  9. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  10. package/dist/channels/origin/origin-channel.js +32 -10
  11. package/dist/channels/origin/types.d.ts +7 -0
  12. package/dist/channels/origin/types.d.ts.map +1 -1
  13. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  14. package/dist/channels/slack/cursor-account.js +2 -2
  15. package/dist/channels/slack/dispatch.d.ts +3 -0
  16. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  17. package/dist/channels/slack/dispatch.js +18 -18
  18. package/dist/channels/slack/interactive.d.ts +2 -0
  19. package/dist/channels/slack/interactive.d.ts.map +1 -1
  20. package/dist/channels/slack/interactive.js +4 -16
  21. package/dist/channels/slack/slack-channel.d.ts +1 -1
  22. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  23. package/dist/channels/slack/slack-channel.js +21 -10
  24. package/dist/channels/slack/types.d.ts +7 -0
  25. package/dist/channels/slack/types.d.ts.map +1 -1
  26. package/dist/channels.d.ts.map +1 -1
  27. package/dist/channels.js +4 -0
  28. package/dist/continuation.d.ts +20 -0
  29. package/dist/continuation.d.ts.map +1 -0
  30. package/dist/continuation.js +18 -0
  31. package/dist/docs/404.html +1 -1
  32. package/dist/docs/ab.html +2 -2
  33. package/dist/docs/assets/{app.e4_wtjUS.js → app.BEHVaPKt.js} +1 -1
  34. package/dist/docs/assets/chunks/{@localSearchIndexroot.BGiMJpnL.js → @localSearchIndexroot.Vvio4stV.js} +1 -1
  35. package/dist/docs/assets/chunks/{VPLocalSearchBox.DbdzyEie.js → VPLocalSearchBox.D2-5PKih.js} +1 -1
  36. package/dist/docs/assets/chunks/{theme.D8HpLrLK.js → theme.uNqJ4MUO.js} +2 -2
  37. package/dist/docs/building-with-agents.html +2 -2
  38. package/dist/docs/concepts.html +2 -2
  39. package/dist/docs/deployment.html +2 -2
  40. package/dist/docs/evals.html +2 -2
  41. package/dist/docs/example-agents/approval-buddy.html +2 -2
  42. package/dist/docs/example-agents/benny.html +2 -2
  43. package/dist/docs/example-agents/bugbot.html +2 -2
  44. package/dist/docs/example-agents/codebase-wiki.html +2 -2
  45. package/dist/docs/example-agents/codeowners-review.html +2 -2
  46. package/dist/docs/example-agents/concierge.html +2 -2
  47. package/dist/docs/example-agents/fsd.html +2 -2
  48. package/dist/docs/example-agents/index.html +2 -2
  49. package/dist/docs/example-agents/knowledge-base.html +2 -2
  50. package/dist/docs/example-agents/oncall.html +2 -2
  51. package/dist/docs/example-agents/security-reviewer.html +2 -2
  52. package/dist/docs/example-agents/slack-agent.html +2 -2
  53. package/dist/docs/example-agents/weather-agent.html +2 -2
  54. package/dist/docs/guides/agent-to-agent.html +2 -2
  55. package/dist/docs/guides/cloud-runtime.html +2 -2
  56. package/dist/docs/guides/convert-automation.html +2 -2
  57. package/dist/docs/guides/github.html +2 -2
  58. package/dist/docs/guides/human-in-the-loop.html +2 -2
  59. package/dist/docs/guides/mcp-oauth.html +2 -2
  60. package/dist/docs/guides/opentelemetry.html +2 -2
  61. package/dist/docs/guides/slack.html +2 -2
  62. package/dist/docs/guides/webhooks.html +2 -2
  63. package/dist/docs/hillclimbing.html +2 -2
  64. package/dist/docs/index.html +2 -2
  65. package/dist/docs/quickstart.html +2 -2
  66. package/dist/docs/reference/agent-config.html +2 -2
  67. package/dist/docs/reference/artifacts.html +2 -2
  68. package/dist/docs/reference/channels.html +2 -2
  69. package/dist/docs/reference/cli.html +2 -2
  70. package/dist/docs/reference/connections.html +2 -2
  71. package/dist/docs/reference/hooks.html +2 -2
  72. package/dist/docs/reference/http-api.html +2 -2
  73. package/dist/docs/reference/instructions.html +2 -2
  74. package/dist/docs/reference/playground.html +2 -2
  75. package/dist/docs/reference/project-layout.html +2 -2
  76. package/dist/docs/reference/prompt.html +2 -2
  77. package/dist/docs/reference/schedules.html +2 -2
  78. package/dist/docs/reference/sessions.html +2 -2
  79. package/dist/docs/reference/skills.html +2 -2
  80. package/dist/docs/reference/subagents.html +2 -2
  81. package/dist/docs/reference/tools.html +2 -2
  82. package/dist/docs/scaffolding-agents.html +2 -2
  83. package/dist/docs/storage.html +2 -2
  84. package/dist/docs/templates/agentic-owners.html +2 -2
  85. package/dist/docs/templates/demo.html +2 -2
  86. package/dist/docs/templates/pr-autofixer.html +2 -2
  87. package/dist/docs/templates/security-reviewer.html +2 -2
  88. package/dist/docs/templates/triage.html +2 -2
  89. package/dist/docs/troubleshooting.html +2 -2
  90. package/dist/files.d.ts +9 -3
  91. package/dist/files.d.ts.map +1 -1
  92. package/dist/files.js +9 -3
  93. package/dist/index.d.ts +5 -1
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +4 -1
  96. package/dist/internal/continuation-channel.d.ts +29 -0
  97. package/dist/internal/continuation-channel.d.ts.map +1 -0
  98. package/dist/internal/continuation-channel.js +88 -0
  99. package/dist/internal/continuation-identity.d.ts +95 -0
  100. package/dist/internal/continuation-identity.d.ts.map +1 -0
  101. package/dist/internal/continuation-identity.js +445 -0
  102. package/dist/internal/deploy-manifest.d.ts +12 -2
  103. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  104. package/dist/internal/deploy-manifest.js +15 -7
  105. package/dist/internal/discovery.d.ts.map +1 -1
  106. package/dist/internal/discovery.js +32 -5
  107. package/dist/internal/server.d.ts.map +1 -1
  108. package/dist/internal/server.js +18 -8
  109. package/dist/internal/session-engine.d.ts +24 -0
  110. package/dist/internal/session-engine.d.ts.map +1 -1
  111. package/dist/internal/session-engine.js +104 -34
  112. package/dist/internal/session-store.d.ts.map +1 -1
  113. package/dist/internal/session-store.js +6 -0
  114. package/dist/internal/skills-sync.d.ts +25 -0
  115. package/dist/internal/skills-sync.d.ts.map +1 -0
  116. package/dist/internal/skills-sync.js +96 -0
  117. package/dist/playground/assets/{index-DJzcIeGt.css → index-ChETdG-_.css} +1 -1
  118. package/dist/playground/assets/{index-K3Sxyx0I.js → index-CkuIrhg4.js} +3 -3
  119. package/dist/playground/index.html +2 -2
  120. package/dist/types.d.ts +44 -0
  121. package/dist/types.d.ts.map +1 -1
  122. package/package.json +1 -1
  123. package/src/channels/github/cursor-account.ts +20 -0
  124. package/src/channels/github/github-channel.ts +52 -1
  125. package/src/channels/github/types.ts +8 -0
  126. package/src/channels/origin/origin-channel.ts +48 -10
  127. package/src/channels/origin/types.ts +7 -0
  128. package/src/channels/slack/cursor-account.ts +1 -0
  129. package/src/channels/slack/dispatch.ts +44 -2
  130. package/src/channels/slack/interactive.ts +5 -0
  131. package/src/channels/slack/slack-channel.ts +23 -2
  132. package/src/channels/slack/types.ts +7 -0
  133. package/src/channels.ts +7 -0
  134. package/src/continuation.ts +49 -0
  135. package/src/files.ts +10 -3
  136. package/src/index.ts +30 -1
  137. package/src/internal/continuation-channel.ts +129 -0
  138. package/src/internal/continuation-identity.ts +597 -0
  139. package/src/internal/deploy-manifest.ts +36 -3
  140. package/src/internal/discovery.ts +40 -2
  141. package/src/internal/server.ts +23 -9
  142. package/src/internal/session-engine.ts +132 -37
  143. package/src/internal/session-store.ts +6 -0
  144. package/src/internal/skills-sync.ts +107 -0
  145. package/src/types.ts +55 -0
@@ -6,6 +6,12 @@ import {
6
6
  loopbackOnly,
7
7
  POST,
8
8
  } from "../../channels.js";
9
+ import {
10
+ bindMailboxContinuationSend,
11
+ continuationSource,
12
+ normalizeIdentity,
13
+ stampMailboxContinuation,
14
+ } from "../../internal/continuation-identity.js";
9
15
  import { isDevMode } from "../../internal/dev-mode.js";
10
16
  import type {
11
17
  AgentCloudOptions,
@@ -443,15 +449,19 @@ function acknowledgeTask(
443
449
  }
444
450
 
445
451
  async function startTurn(input: {
452
+ options: OriginChannelOptions;
446
453
  args: ChannelHandlerArgs;
447
454
  ctx: OriginEventContext;
448
455
  message: string;
449
456
  dispatch: OriginTurnDispatch;
450
457
  cloud?: AgentCloudOptions;
451
458
  }): Promise<Response> {
459
+ const source = continuationSource(input.options.continuation, "origin");
452
460
  const session = await input.args.send(input.message, {
453
461
  auth: input.dispatch.auth,
454
- continuationToken: originContinuationKey(input.ctx),
462
+ ...(source === "engine"
463
+ ? { continuationToken: originContinuationKey(input.ctx) }
464
+ : {}),
455
465
  title: input.dispatch.title ?? defaultOriginTitle(input.ctx),
456
466
  ...(input.cloud === undefined ? {} : { cloud: input.cloud }),
457
467
  });
@@ -489,6 +499,7 @@ async function resolveCloudOptions(
489
499
  }
490
500
 
491
501
  async function applyDispatch(input: {
502
+ options: OriginChannelOptions;
492
503
  args: ChannelHandlerArgs;
493
504
  ctx: OriginEventContext;
494
505
  dispatch: OriginDispatch | null;
@@ -507,6 +518,7 @@ async function applyDispatch(input: {
507
518
  ...(input.dispatch.context ?? []),
508
519
  ].join("\n");
509
520
  return startTurn({
521
+ options: input.options,
510
522
  args: input.args,
511
523
  ctx: input.ctx,
512
524
  message,
@@ -533,9 +545,21 @@ export async function dispatchOriginWebhook(input: {
533
545
  { status: 400 }
534
546
  );
535
547
  }
548
+ const identity = normalizeIdentity(input.options.continuation, "origin");
549
+ let args = input.args;
550
+ if (continuationSource(identity, "origin") === "control-plane") {
551
+ const mailbox = stampMailboxContinuation(identity, eventType, {
552
+ pullRequest: { number: parsed.ctx.pullRequestNumber },
553
+ repository: { fullName: parsed.ctx.repository.fullName },
554
+ });
555
+ args = {
556
+ ...input.args,
557
+ send: bindMailboxContinuationSend(input.args.send, mailbox),
558
+ };
559
+ }
536
560
  const ctx = enrichEventContext({
537
561
  ctx: parsed.ctx,
538
- args: input.args,
562
+ args,
539
563
  options: input.options,
540
564
  });
541
565
  const { pullRequest, payload } = parsed;
@@ -568,7 +592,8 @@ export async function dispatchOriginWebhook(input: {
568
592
  };
569
593
  const dispatch = await input.options.onComment(ctx, comment);
570
594
  return applyDispatch({
571
- args: input.args,
595
+ options: input.options,
596
+ args,
572
597
  ctx,
573
598
  dispatch,
574
599
  extra: { event: eventType },
@@ -588,7 +613,8 @@ export async function dispatchOriginWebhook(input: {
588
613
  };
589
614
  const dispatch = await input.options.onReview(ctx, review);
590
615
  return applyDispatch({
591
- args: input.args,
616
+ options: input.options,
617
+ args,
592
618
  ctx,
593
619
  dispatch,
594
620
  extra: { event: eventType },
@@ -607,7 +633,8 @@ export async function dispatchOriginWebhook(input: {
607
633
  pullRequest
608
634
  );
609
635
  return applyDispatch({
610
- args: input.args,
636
+ options: input.options,
637
+ args,
611
638
  ctx,
612
639
  dispatch,
613
640
  extra: { event: eventType },
@@ -622,7 +649,8 @@ export async function dispatchOriginWebhook(input: {
622
649
  payload,
623
650
  });
624
651
  return applyDispatch({
625
- args: input.args,
652
+ options: input.options,
653
+ args,
626
654
  ctx,
627
655
  dispatch,
628
656
  extra: { event: eventType },
@@ -725,13 +753,23 @@ export function originChannel(
725
753
  : localDevStrict())(request, info),
726
754
  ];
727
755
 
756
+ const channelOptions: OriginChannelOptions = {
757
+ ...options,
758
+ };
759
+ if (channelOptions.continuation !== undefined) {
760
+ channelOptions.continuation = normalizeIdentity(
761
+ channelOptions.continuation,
762
+ "origin"
763
+ );
764
+ }
765
+
728
766
  const definition = defineChannel({
729
767
  auth,
730
768
  onStart: options.onStart,
731
769
  onStop: options.onStop,
732
770
  meta: {
733
771
  kind: ORIGIN_CHANNEL_KIND,
734
- originWebhookEvents: deriveOriginWebhookEvents(options),
772
+ originWebhookEvents: deriveOriginWebhookEvents(channelOptions),
735
773
  },
736
774
  routes: [
737
775
  POST(routePath, {
@@ -739,17 +777,17 @@ export function originChannel(
739
777
  description: "Origin webhook ingress",
740
778
  handler: async (request, args) =>
741
779
  handleOriginWebhookRequest({
742
- options,
780
+ options: channelOptions,
743
781
  request,
744
782
  args,
745
783
  }),
746
784
  }),
747
785
  ],
748
786
  });
749
- originChannelOptionsByDefinition.set(definition, options);
787
+ originChannelOptionsByDefinition.set(definition, channelOptions);
750
788
  if (usesCursorAccount) {
751
789
  registerOriginCursorAccountRuntime(definition, {
752
- options,
790
+ options: channelOptions,
753
791
  scope: cursorScope,
754
792
  });
755
793
  }
@@ -1,3 +1,4 @@
1
+ import type { ContinuationIdentity } from "../../continuation.js";
1
2
  import type {
2
3
  AgentCloudOptions,
3
4
  ArtifactsApi,
@@ -195,6 +196,12 @@ export interface OriginChannelOptions {
195
196
  * `meta.originWebhookEvents`). Defaults to the set derived from hooks.
196
197
  */
197
198
  webhookEvents?: readonly string[];
199
+ /**
200
+ * Continuation selector. Omitted inherits {@link AgentConfig.architecture}:
201
+ * `"origin.pr/v1"` unless the agent set `architecture: "v2"` (then
202
+ * `"origin.pr/v2"`). Override with `"origin.none/v2"` for one-shot events.
203
+ */
204
+ continuation?: ContinuationIdentity;
198
205
  /**
199
206
  * Decide whether an inbound pull-request event starts a turn. Return
200
207
  * `{ auth: defaultOriginAuth(ctx) }` to dispatch, `null` to ignore.
@@ -196,6 +196,7 @@ export async function dispatchCursorSlackEnvelope(args: {
196
196
  ...(args.interruptContinuation === undefined
197
197
  ? {}
198
198
  : { interruptContinuation: args.interruptContinuation }),
199
+ continuation: runtime.options.continuation,
199
200
  ...(envelope.eventId === undefined ? {} : { eventId: envelope.eventId }),
200
201
  });
201
202
  }
@@ -2,6 +2,13 @@
2
2
  * Shared inbound dispatch for Slack Events API bodies and Socket Mode.
3
3
  */
4
4
 
5
+ import {
6
+ type ContinuationIdentity,
7
+ continuationSource,
8
+ type MailboxContinuation,
9
+ normalizeIdentity,
10
+ stampMailboxContinuation,
11
+ } from "../../internal/continuation-identity.js";
5
12
  import type {
6
13
  ArtifactsApi,
7
14
  HostContext,
@@ -121,6 +128,7 @@ export function emptySlackChannelState(
121
128
 
122
129
  export interface SlackInboundDispatchDeps extends SlackContinuationProbes {
123
130
  send: SendMessageFn;
131
+ continuation?: ContinuationIdentity;
124
132
  waitUntil: (promise: Promise<unknown>) => void;
125
133
  onAppMention: SlackMessageHandler;
126
134
  onDirectMessage: SlackMessageHandler;
@@ -180,6 +188,35 @@ function triggeringMergeMessage(
180
188
  };
181
189
  }
182
190
 
191
+ function continuationArg(identity: ContinuationIdentity | undefined): {
192
+ continuation?: ContinuationIdentity;
193
+ } {
194
+ return identity === undefined ? {} : { continuation: identity };
195
+ }
196
+
197
+ function slackSessionContinuation(
198
+ identity: ContinuationIdentity | undefined,
199
+ channelId: string,
200
+ threadTs: string
201
+ ): {
202
+ probeKey: string;
203
+ sendPatch:
204
+ | { continuationToken: string }
205
+ | { mailboxContinuation: MailboxContinuation };
206
+ } {
207
+ if (continuationSource(identity, "slack") === "engine") {
208
+ const key = slackContinuationKey(channelId, threadTs);
209
+ return { probeKey: key, sendPatch: { continuationToken: key } };
210
+ }
211
+ const mailbox = stampMailboxContinuation(
212
+ normalizeIdentity(identity, "slack"),
213
+ "message",
214
+ { channelId, threadTs },
215
+ "slack"
216
+ );
217
+ return { probeKey: mailbox.key, sendPatch: { mailboxContinuation: mailbox } };
218
+ }
219
+
183
220
  export async function dispatchInboundMessage(args: {
184
221
  kind:
185
222
  | "app_mention"
@@ -210,6 +247,7 @@ export async function dispatchInboundMessage(args: {
210
247
  */
211
248
  cursorAgentName?: string;
212
249
  blockExternals?: boolean;
250
+ continuation?: ContinuationIdentity;
213
251
  }): Promise<void> {
214
252
  const { message, kind } = args;
215
253
  const log = args.log ?? defaultSlackLog;
@@ -265,7 +303,8 @@ export async function dispatchInboundMessage(args: {
265
303
  }
266
304
  }
267
305
 
268
- const continuation = slackContinuationKey(
306
+ const { probeKey: continuation, sendPatch } = slackSessionContinuation(
307
+ args.continuation,
269
308
  message.channelId,
270
309
  message.threadTs
271
310
  );
@@ -454,7 +493,7 @@ export async function dispatchInboundMessage(args: {
454
493
 
455
494
  const sendOptions = {
456
495
  auth: decision.auth,
457
- continuationToken: continuation,
496
+ ...sendPatch,
458
497
  admission: "coalesce" as const,
459
498
  coalesceSourceTs: message.ts,
460
499
  state: emptySlackChannelState({
@@ -617,6 +656,7 @@ export function dispatchInboundFromEventBody(
617
656
  cursorAgentName: deps.cursorAgentName,
618
657
  followUpStatus: deps.followUpStatus,
619
658
  blockExternals: deps.blockExternals,
659
+ ...continuationArg(deps.continuation),
620
660
  ...(deps.hasContinuationSession === undefined
621
661
  ? {}
622
662
  : { hasContinuationSession: deps.hasContinuationSession }),
@@ -660,6 +700,7 @@ export function dispatchInboundFromEventBody(
660
700
  cursorAgentName: deps.cursorAgentName,
661
701
  followUpStatus: deps.followUpStatus,
662
702
  blockExternals: deps.blockExternals,
703
+ ...continuationArg(deps.continuation),
663
704
  ...(deps.hasContinuationSession === undefined
664
705
  ? {}
665
706
  : { hasContinuationSession: deps.hasContinuationSession }),
@@ -697,6 +738,7 @@ export function dispatchInboundFromEventBody(
697
738
  cursorAgentName: deps.cursorAgentName,
698
739
  followUpStatus: deps.followUpStatus,
699
740
  blockExternals: deps.blockExternals,
741
+ ...continuationArg(deps.continuation),
700
742
  ...(deps.hasContinuationSession === undefined
701
743
  ? {}
702
744
  : { hasContinuationSession: deps.hasContinuationSession }),
@@ -7,6 +7,7 @@
7
7
  * `slackChannel({ onBlockAction })`).
8
8
  */
9
9
 
10
+ import type { ContinuationIdentity } from "../../internal/continuation-identity.js";
10
11
  import type {
11
12
  ApprovalDecision,
12
13
  ArtifactsApi,
@@ -298,6 +299,7 @@ export async function resolveBlockActionCard(args: {
298
299
  export interface SlackBlockActionDispatchDeps extends SlackContinuationProbes {
299
300
  handler: SlackBlockActionHandler;
300
301
  send: SendMessageFn;
302
+ continuation?: ContinuationIdentity;
301
303
  credentials: SlackCredentials | undefined;
302
304
  host: HostContext;
303
305
  artifacts: ArtifactsApi;
@@ -389,6 +391,9 @@ export async function dispatchBlockAction(
389
391
  isContinuationBusy: deps.isContinuationBusy,
390
392
  getContinuationLastBotMessageTs: deps.getContinuationLastBotMessageTs,
391
393
  interruptContinuation: deps.interruptContinuation,
394
+ ...(deps.continuation === undefined
395
+ ? {}
396
+ : { continuation: deps.continuation }),
392
397
  });
393
398
  return true;
394
399
  }
@@ -8,6 +8,10 @@
8
8
  */
9
9
 
10
10
  import { allowAll, defineChannel } from "../../channels.js";
11
+ import {
12
+ continuationSource,
13
+ normalizeIdentity,
14
+ } from "../../internal/continuation-identity.js";
11
15
  import type {
12
16
  ArtifactsApi,
13
17
  ChannelDefinition,
@@ -126,10 +130,14 @@ export function resolveDedicatedSlackHostBinding(
126
130
  * runtime values are still checked by {@link assertSlackCursorAgentName}.
127
131
  */
128
132
  export function slackChannel<const Name extends string = string>(
129
- options: Omit<SlackChannelOptions, "agentName"> & {
133
+ authored: Omit<SlackChannelOptions, "agentName"> & {
130
134
  agentName?: SlackCursorAgentName<Name>;
131
135
  } = {}
132
136
  ): ChannelDefinition<SlackChannelState> {
137
+ const options = { ...authored };
138
+ if (options.continuation !== undefined) {
139
+ options.continuation = normalizeIdentity(options.continuation, "slack");
140
+ }
133
141
  const cursorAccount = options.cursorAccount === true;
134
142
  if (cursorAccount) {
135
143
  // Token pair and the Cursor connection are different Slack apps —
@@ -285,6 +293,7 @@ export function slackChannel<const Name extends string = string>(
285
293
  }): SlackInboundDispatchDeps {
286
294
  return {
287
295
  send: args.send,
296
+ continuation: options.continuation,
288
297
  waitUntil: args.waitUntil,
289
298
  onAppMention,
290
299
  onDirectMessage,
@@ -359,7 +368,17 @@ export function slackChannel<const Name extends string = string>(
359
368
  return send(input.message, {
360
369
  auth: input.auth,
361
370
  admission: "coalesce",
362
- continuationToken: slackContinuationKey(channelId, threadTs),
371
+ ...(continuationSource(options.continuation, "slack") ===
372
+ "control-plane"
373
+ ? {
374
+ claim: {
375
+ type: "thread" as const,
376
+ resource: { channelId, threadTs },
377
+ },
378
+ }
379
+ : {
380
+ continuationToken: slackContinuationKey(channelId, threadTs),
381
+ }),
363
382
  state: emptySlackChannelState({
364
383
  channelId,
365
384
  threadTs,
@@ -488,6 +507,7 @@ export function slackChannel<const Name extends string = string>(
488
507
  evalCommandsEnabled,
489
508
  followUpStatus,
490
509
  blockExternals,
510
+ continuation: options.continuation,
491
511
  ...(args.hasContinuationSession === undefined
492
512
  ? {}
493
513
  : {
@@ -583,6 +603,7 @@ export function slackChannel<const Name extends string = string>(
583
603
  getContinuationLastBotMessageTs:
584
604
  args.getContinuationLastBotMessageTs,
585
605
  interruptContinuation: args.interruptContinuation,
606
+ continuation: options.continuation,
586
607
  });
587
608
  };
588
609
  }
@@ -2,6 +2,7 @@
2
2
  * Types for the Slack platform channel pack.
3
3
  */
4
4
 
5
+ import type { ContinuationIdentity } from "../../continuation.js";
5
6
  import type {
6
7
  AgentCloudOptions,
7
8
  ArtifactsApi,
@@ -490,6 +491,12 @@ export interface SlackAccessPolicy {
490
491
 
491
492
  export interface SlackChannelOptions {
492
493
  credentials?: SlackCredentials;
494
+ /**
495
+ * Continuation selector. Omitted inherits {@link AgentConfig.architecture}:
496
+ * `"slack.thread/v1"` unless the agent set `architecture: "v2"` (then
497
+ * `"slack.thread/v2"`). Override with `"slack.none/v2"` for one-shot events.
498
+ */
499
+ continuation?: ContinuationIdentity;
493
500
  /** Defaults to `{ respondTo: "thread", blockExternals: true }`. */
494
501
  policy?: SlackAccessPolicy;
495
502
  /**
package/src/channels.ts CHANGED
@@ -66,6 +66,7 @@ export {
66
66
  import { Buffer } from "node:buffer";
67
67
  import { timingSafeEqual } from "node:crypto";
68
68
  import { brandDefinition } from "./internal/brand.js";
69
+ import { normalizeIdentity } from "./internal/continuation-identity.js";
69
70
  import { isLoopbackAddress, isLoopbackHostname } from "./internal/net.js";
70
71
  import type {
71
72
  AuthContext,
@@ -127,6 +128,12 @@ export function defineChannel<TState = JsonValue>(
127
128
  export function httpChannel(
128
129
  config: HttpChannelConfig = {}
129
130
  ): HttpChannelDefinition {
131
+ if (config.continuation !== undefined) {
132
+ return brandDefinition("http-channel", {
133
+ ...config,
134
+ continuation: normalizeIdentity(config.continuation, "http"),
135
+ });
136
+ }
130
137
  return brandDefinition("http-channel", config);
131
138
  }
132
139
 
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Public continuation-identity surface for `@cursor/july`.
3
+ *
4
+ * Omitted `continuation` on a channel constructor stays `/v1` (engine
5
+ * minting). Deploy onto the v2 control plane with a root architecture
6
+ * flag; omitted channels then inherit the kind `/v2` default:
7
+ *
8
+ * ```ts
9
+ * export default defineAgent({
10
+ * architecture: "v2",
11
+ * });
12
+ * ```
13
+ *
14
+ * Per-channel selectors still override type (`"github.issue/v2"`,
15
+ * `"github.none/v2"`). Other `/v2` selectors: `"slack.thread/v2"`,
16
+ * `"origin.pr/v2"`, `"none/v2"`.
17
+ */
18
+
19
+ export type {
20
+ ContinuationBuiltin,
21
+ ContinuationClaim,
22
+ ContinuationIdentity,
23
+ ContinuationProtocolVersion,
24
+ ContinuationSelector,
25
+ ConversationType,
26
+ MailboxContinuation,
27
+ ParsedContinuation,
28
+ } from "./internal/continuation-identity.js";
29
+ export {
30
+ BUILTINS,
31
+ bindMailboxContinuationSend,
32
+ builtinForType,
33
+ CONTINUATION_SELECTORS,
34
+ continuationSource,
35
+ defaultContinuationSelector,
36
+ extractContinuation,
37
+ formatConversationKey,
38
+ handshakeContinuation,
39
+ isContinuationSelector,
40
+ isV2DurableContinuationKey,
41
+ mintClaim,
42
+ newEvtKey,
43
+ normalizeIdentity,
44
+ parseContinuationSelector,
45
+ resolveContinuation,
46
+ resolveV2Key,
47
+ stampMailboxContinuation,
48
+ V2EngineError,
49
+ } from "./internal/continuation-identity.js";
package/src/files.ts CHANGED
@@ -142,9 +142,13 @@ export const fileKeys = {
142
142
 
143
143
  /**
144
144
  * Framework-owned keys for state that lives on the deployment's Agent Store
145
- * but is not author `host.files` data: artifact rows and payloads, and the
146
- * cross-session memory journal. Kept out of `files/` so author listings never
147
- * see framework rows.
145
+ * but is not author `host.files` data: artifact rows and payloads, authored
146
+ * skills synced for cloud turns, and the cross-session memory journal. Kept
147
+ * out of `files/` so author listings never see framework rows.
148
+ *
149
+ * Skills sit at store-root `skills/<name>/…` so exec-daemon's
150
+ * `--agent-store-skills-dir` walk finds them the same way it finds user-store
151
+ * skills. Memory and artifacts stay under {@link STORAGE_KEY_ROOT}.
148
152
  */
149
153
  export const agentStoreKeys = {
150
154
  artifactRow: (agent: string, id: string): string =>
@@ -153,6 +157,9 @@ export const agentStoreKeys = {
153
157
  `${STORAGE_KEY_ROOT}/${agent}/artifacts/rows/`,
154
158
  artifactContent: (agent: string, id: string): string =>
155
159
  `${STORAGE_KEY_ROOT}/${agent}/artifacts/content/${encodeURIComponent(id)}`,
160
+ skillFile: (skillName: string, relPath: string): string =>
161
+ `skills/${skillName}/${relPath}`,
162
+ skillsPrefix: (): string => "skills/",
156
163
  memoryDir: (agent: string): string => `${STORAGE_KEY_ROOT}/${agent}/memory`,
157
164
  memoryJournal: (agent: string): string =>
158
165
  `${STORAGE_KEY_ROOT}/${agent}/memory/journal.jsonl`,
package/src/index.ts CHANGED
@@ -51,6 +51,33 @@ export {
51
51
  publicEndpoint,
52
52
  } from "./channels.js";
53
53
  export { defineConnection } from "./connections.js";
54
+ export type {
55
+ ContinuationBuiltin,
56
+ ContinuationClaim,
57
+ ContinuationIdentity,
58
+ ContinuationSelector,
59
+ ConversationType,
60
+ MailboxContinuation,
61
+ ParsedContinuation,
62
+ } from "./continuation.js";
63
+ export {
64
+ BUILTINS,
65
+ bindMailboxContinuationSend,
66
+ CONTINUATION_SELECTORS,
67
+ continuationSource,
68
+ defaultContinuationSelector,
69
+ extractContinuation,
70
+ formatConversationKey,
71
+ handshakeContinuation,
72
+ isContinuationSelector,
73
+ mintClaim,
74
+ normalizeIdentity,
75
+ parseContinuationSelector,
76
+ resolveContinuation,
77
+ resolveV2Key,
78
+ stampMailboxContinuation,
79
+ V2EngineError,
80
+ } from "./continuation.js";
54
81
  export { defineHook } from "./hooks.js";
55
82
  export type { LoadAgentProjectOptions } from "./internal/discovery.js";
56
83
  export {
@@ -162,7 +189,9 @@ export {
162
189
 
163
190
  /**
164
191
  * Runtime config for an agent — authored at `agent/agent.ts` (the root
165
- * agent) or `agent/subagents/<id>/agent.ts` (a subagent).
192
+ * agent) or `agent/subagents/<id>/agent.ts` (a subagent). Set
193
+ * `architecture: "v2"` to deploy onto the v2 control plane; omitted
194
+ * channel continuation then inherits kind `/v2` defaults.
166
195
  */
167
196
  export function defineAgent(config: AgentConfig): AgentConfigDefinition {
168
197
  return brandDefinition("agent", config);
@@ -0,0 +1,129 @@
1
+ import { getGitHubChannelOptions } from "../channels/github/github-channel.js";
2
+ import { getOriginChannelOptions } from "../channels/origin/origin-channel.js";
3
+ import { getSlackChannelOptions } from "../channels/slack/slack-channel.js";
4
+ import type {
5
+ AgentArchitecture,
6
+ ChannelDefinition,
7
+ HttpChannelDefinition,
8
+ } from "../types.js";
9
+ import {
10
+ type ContinuationIdentity,
11
+ type ContinuationProtocolVersion,
12
+ defaultContinuationSelector,
13
+ handshakeContinuation,
14
+ normalizeIdentity,
15
+ } from "./continuation-identity.js";
16
+
17
+ export function channelContinuationIdentity(
18
+ definition: object | undefined
19
+ ): ContinuationIdentity {
20
+ if (definition === undefined) {
21
+ return defaultContinuationSelector();
22
+ }
23
+ const github = getGitHubChannelOptions(definition);
24
+ if (github !== undefined) {
25
+ return normalizeIdentity(github.continuation, "github");
26
+ }
27
+ const slack = getSlackChannelOptions(definition);
28
+ if (slack !== undefined) {
29
+ return normalizeIdentity(slack.continuation, "slack");
30
+ }
31
+ const origin = getOriginChannelOptions(definition);
32
+ if (origin !== undefined) {
33
+ return normalizeIdentity(origin.continuation, "origin");
34
+ }
35
+ return defaultContinuationSelector();
36
+ }
37
+
38
+ export function httpContinuationIdentity(
39
+ httpChannel: HttpChannelDefinition | undefined
40
+ ): ContinuationIdentity {
41
+ return normalizeIdentity(httpChannel?.continuation, "http");
42
+ }
43
+
44
+ /**
45
+ * Stamp `/v2` continuation defaults onto channel constructors that omitted
46
+ * `continuation` when the agent deploys onto v2 architecture. GitHub/Origin
47
+ * type (`pr` / `issue` / `none`) follows declared webhook events. Explicit
48
+ * per-channel selectors are left alone. Idempotent.
49
+ */
50
+ export function applyAgentContinuationDefaults(project: {
51
+ agent: { architecture?: AgentArchitecture };
52
+ channels: ReadonlyArray<{ definition: object }>;
53
+ httpChannel: HttpChannelDefinition;
54
+ }): void {
55
+ if (project.agent.architecture !== "v2") {
56
+ return;
57
+ }
58
+ for (const channel of project.channels) {
59
+ stampOmittedChannelContinuation(channel.definition, "v2");
60
+ }
61
+ if (project.httpChannel.continuation === undefined) {
62
+ project.httpChannel.continuation = defaultContinuationSelector(
63
+ "http",
64
+ "v2"
65
+ );
66
+ }
67
+ }
68
+
69
+ function stampOmittedChannelContinuation(
70
+ definition: object,
71
+ version: ContinuationProtocolVersion
72
+ ): void {
73
+ const events = declaredChannelEventTypes(definition);
74
+ const github = getGitHubChannelOptions(definition);
75
+ if (github !== undefined) {
76
+ if (github.continuation === undefined) {
77
+ github.continuation = defaultContinuationSelector(
78
+ "github",
79
+ version,
80
+ events
81
+ );
82
+ }
83
+ return;
84
+ }
85
+ const slack = getSlackChannelOptions(definition);
86
+ if (slack !== undefined) {
87
+ if (slack.continuation === undefined) {
88
+ slack.continuation = defaultContinuationSelector("slack", version);
89
+ }
90
+ return;
91
+ }
92
+ const origin = getOriginChannelOptions(definition);
93
+ if (origin !== undefined) {
94
+ if (origin.continuation === undefined) {
95
+ origin.continuation = defaultContinuationSelector(
96
+ "origin",
97
+ version,
98
+ events
99
+ );
100
+ }
101
+ }
102
+ }
103
+
104
+ function declaredChannelEventTypes(
105
+ definition: object
106
+ ): readonly string[] | undefined {
107
+ const meta = (definition as ChannelDefinition).meta;
108
+ return meta?.githubWebhookEvents ?? meta?.originWebhookEvents;
109
+ }
110
+
111
+ export function handshakeChannelContinuation(input: {
112
+ id: string;
113
+ definition: ChannelDefinition;
114
+ }): { ok: true } | { ok: false; error: string } {
115
+ const kind = input.definition.meta?.kind;
116
+ const eventTypes = [
117
+ ...(input.definition.meta?.githubWebhookEvents ?? []),
118
+ ...(input.definition.meta?.originWebhookEvents ?? []),
119
+ ];
120
+ const result = handshakeContinuation({
121
+ kind,
122
+ eventTypes,
123
+ identity: channelContinuationIdentity(input.definition),
124
+ });
125
+ if (!result.ok) {
126
+ return { ok: false, error: `channel ${input.id}: ${result.error}` };
127
+ }
128
+ return result;
129
+ }