@cursor/july 0.1.114 → 0.2.0

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 (160) hide show
  1. package/dist/channels/slack/inbound.d.ts +7 -0
  2. package/dist/channels/slack/inbound.d.ts.map +1 -1
  3. package/dist/channels/slack/inbound.js +23 -0
  4. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  5. package/dist/channels/slack/slack-channel.js +83 -4
  6. package/dist/docs/404.html +2 -2
  7. package/dist/docs/assets/{app.BqkJwOZ-.js → app.9eAtsjAM.js} +4 -4
  8. package/dist/docs/assets/chunks/@localSearchIndexroot.DjNBgxTF.js +1 -0
  9. package/dist/docs/assets/chunks/{VPLocalSearchBox.BJAi2KiV.js → VPLocalSearchBox.C9s69nxj.js} +1 -1
  10. package/dist/docs/assets/chunks/{arc.BZpXTgvV.js → arc.DmWDRaF-.js} +1 -1
  11. package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.WYI-7F-Y.js → architectureDiagram-Q4EWVU46.BE1Wj4f3.js} +1 -1
  12. package/dist/docs/assets/chunks/{baseUniq.CZaUPpg0.js → baseUniq.pmEZGnWu.js} +1 -1
  13. package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.D6UES2pD.js → blockDiagram-DXYQGD6D.D6mM-5XN.js} +1 -1
  14. package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.cwebIe4i.js → c4Diagram-AHTNJAMY.ZQYTPC1b.js} +1 -1
  15. package/dist/docs/assets/chunks/channel.BNF8VK-B.js +1 -0
  16. package/dist/docs/assets/chunks/{chunk-4BX2VUAB.fVyFnjxg.js → chunk-4BX2VUAB.CKJ7gaK8.js} +1 -1
  17. package/dist/docs/assets/chunks/{chunk-4TB4RGXK.BanufG1c.js → chunk-4TB4RGXK.CL61HnvB.js} +1 -1
  18. package/dist/docs/assets/chunks/{chunk-55IACEB6.VaSMz5-2.js → chunk-55IACEB6.DsA4XA1m.js} +1 -1
  19. package/dist/docs/assets/chunks/{chunk-EDXVE4YY.CN2diZOM.js → chunk-EDXVE4YY.BODHCHFW.js} +1 -1
  20. package/dist/docs/assets/chunks/{chunk-FMBD7UC4.g4ivypu3.js → chunk-FMBD7UC4.D6NBaZTa.js} +1 -1
  21. package/dist/docs/assets/chunks/{chunk-OYMX7WX6.GZXKn9JJ.js → chunk-OYMX7WX6.BLPuqFXg.js} +1 -1
  22. package/dist/docs/assets/chunks/{chunk-QZHKN3VN.itXxJZCd.js → chunk-QZHKN3VN.BFs_ML8Z.js} +1 -1
  23. package/dist/docs/assets/chunks/{chunk-YZCP3GAM.-rw2GfvX.js → chunk-YZCP3GAM.BDjuFyZv.js} +1 -1
  24. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.iViHzfrK.js +1 -0
  25. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.iViHzfrK.js +1 -0
  26. package/dist/docs/assets/chunks/clone.CJoR2BBM.js +1 -0
  27. package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.CmaI5br0.js → cose-bilkent-S5V4N54A.D85HNEsi.js} +1 -1
  28. package/dist/docs/assets/chunks/{dagre-KV5264BT.4wY9S4Kt.js → dagre-KV5264BT.V463KlSF.js} +1 -1
  29. package/dist/docs/assets/chunks/{diagram-5BDNPKRD.Pc3c0u9W.js → diagram-5BDNPKRD.DhBDu1ab.js} +1 -1
  30. package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.CYrWz-nj.js → diagram-G4DWMVQ6.v6sC68zl.js} +1 -1
  31. package/dist/docs/assets/chunks/{diagram-MMDJMWI5.Bgj5hukb.js → diagram-MMDJMWI5.DhHsdXYv.js} +1 -1
  32. package/dist/docs/assets/chunks/{diagram-TYMM5635.DGMEXalS.js → diagram-TYMM5635.BPWNSFZc.js} +1 -1
  33. package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.GepTV9Im.js → erDiagram-SMLLAGMA.Cq0wTMPL.js} +1 -1
  34. package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.DVKywg3j.js → flowDiagram-DWJPFMVM.MveMtucC.js} +1 -1
  35. package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.C7qt9Mlo.js → ganttDiagram-T4ZO3ILL.BzKfGUSf.js} +1 -1
  36. package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.U30_r82P.js → gitGraphDiagram-UUTBAWPF.OToXTSW_.js} +1 -1
  37. package/dist/docs/assets/chunks/{graph.CyyMyAWv.js → graph.D82tam-l.js} +1 -1
  38. package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.Dn9ACW3y.js → infoDiagram-42DDH7IO.DzFlRmcE.js} +1 -1
  39. package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.DlIdIGOA.js → ishikawaDiagram-UXIWVN3A.CVPRJiKe.js} +1 -1
  40. package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.DZj4vy4E.js → journeyDiagram-VCZTEJTY.CQvNTfQC.js} +1 -1
  41. package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.Dl63eMUV.js → kanban-definition-6JOO6SKY.BwywywUl.js} +1 -1
  42. package/dist/docs/assets/chunks/{layout.BLHZLWPH.js → layout.C4BkPPba.js} +1 -1
  43. package/dist/docs/assets/chunks/{linear.aXKGKaNw.js → linear.FoSfGKD4.js} +1 -1
  44. package/dist/docs/assets/chunks/{min.zWnFcpcc.js → min.yLh8jqfl.js} +1 -1
  45. package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.Qs4MQBea.js → mindmap-definition-QFDTVHPH.C9Dq2_CN.js} +1 -1
  46. package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.BmPHgsk7.js → pieDiagram-DEJITSTG.CiGyBcES.js} +1 -1
  47. package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.D5MQ3gwA.js → quadrantDiagram-34T5L4WZ.1FWea9nj.js} +1 -1
  48. package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.CkdUFrO7.js → requirementDiagram-MS252O5E.B1q7ntiV.js} +1 -1
  49. package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.KZrljrAV.js → sankeyDiagram-XADWPNL6.Cpr4oU_k.js} +1 -1
  50. package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.XMoEW-Lx.js → sequenceDiagram-FGHM5R23.CUK68STn.js} +1 -1
  51. package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.BmTzePLj.js → stateDiagram-FHFEXIEX.zU0yL2m4.js} +1 -1
  52. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.nV8Nl-td.js +1 -0
  53. package/dist/docs/assets/chunks/{theme.BfQzpxsg.js → theme.DhnKd0CD.js} +2 -2
  54. package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.Dug0oamp.js → timeline-definition-GMOUNBTQ.CNYoXo3F.js} +1 -1
  55. package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.BOTHrEFu.js → vennDiagram-DHZGUBPP.DFWH3G9s.js} +1 -1
  56. package/dist/docs/assets/chunks/{wardley-RL74JXVD.DXy2i1LS.js → wardley-RL74JXVD.Do4PwzeN.js} +1 -1
  57. package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.CoXKdfi6.js → wardleyDiagram-NUSXRM2D.7WLkqM7s.js} +1 -1
  58. package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.DXoSCjAW.js → xychartDiagram-5P7HB3ND.-SyUNgWw.js} +1 -1
  59. package/dist/docs/assets/{guides_slack.md.Bo96y42E.js → guides_slack.md.DVjNyqq5.js} +3 -3
  60. package/dist/docs/assets/{guides_slack.md.Bo96y42E.lean.js → guides_slack.md.DVjNyqq5.lean.js} +1 -1
  61. package/dist/docs/assets/reference_cli.md.BvnQM8wd.js +97 -0
  62. package/dist/docs/assets/reference_cli.md.BvnQM8wd.lean.js +1 -0
  63. package/dist/docs/building-with-agents.html +35 -35
  64. package/dist/docs/deployment.html +35 -35
  65. package/dist/docs/evals.html +35 -35
  66. package/dist/docs/guides/agent-to-agent.html +35 -35
  67. package/dist/docs/guides/bitbucket.html +35 -35
  68. package/dist/docs/guides/cloud-agents.html +35 -35
  69. package/dist/docs/guides/convert-automation.html +35 -35
  70. package/dist/docs/guides/github.html +35 -35
  71. package/dist/docs/guides/gitlab.html +35 -35
  72. package/dist/docs/guides/grokbot-agents.html +35 -35
  73. package/dist/docs/guides/hooks.html +35 -35
  74. package/dist/docs/guides/improve.html +35 -35
  75. package/dist/docs/guides/jev.html +35 -35
  76. package/dist/docs/guides/mcp-oauth.html +35 -35
  77. package/dist/docs/guides/opentelemetry.html +35 -35
  78. package/dist/docs/guides/slack.html +37 -37
  79. package/dist/docs/guides/slack.md +7 -0
  80. package/dist/docs/guides/webhooks.html +35 -35
  81. package/dist/docs/hashmap.json +1 -1
  82. package/dist/docs/hillclimbing.html +35 -35
  83. package/dist/docs/index.html +35 -35
  84. package/dist/docs/llms-full.txt +690 -751
  85. package/dist/docs/llms.txt +1 -1
  86. package/dist/docs/quickstart.html +35 -35
  87. package/dist/docs/reference/agent-config.html +35 -35
  88. package/dist/docs/reference/artifacts.html +35 -35
  89. package/dist/docs/reference/channels.html +35 -35
  90. package/dist/docs/reference/cli.html +127 -125
  91. package/dist/docs/reference/cli.md +685 -753
  92. package/dist/docs/reference/connections.html +35 -35
  93. package/dist/docs/reference/evals.html +35 -35
  94. package/dist/docs/reference/extensions.html +35 -35
  95. package/dist/docs/reference/hooks.html +35 -35
  96. package/dist/docs/reference/http-api.html +35 -35
  97. package/dist/docs/reference/instructions.html +35 -35
  98. package/dist/docs/reference/playground.html +35 -35
  99. package/dist/docs/reference/project-layout.html +35 -35
  100. package/dist/docs/reference/prompt.html +35 -35
  101. package/dist/docs/reference/schedules.html +35 -35
  102. package/dist/docs/reference/sessions.html +35 -35
  103. package/dist/docs/reference/skills.html +35 -35
  104. package/dist/docs/reference/subagents.html +35 -35
  105. package/dist/docs/reference/tools.html +35 -35
  106. package/dist/docs/templates/agentic-owners.html +35 -35
  107. package/dist/docs/templates/pr-autofixer.html +35 -35
  108. package/dist/docs/templates/security-reviewer.html +35 -35
  109. package/dist/docs/templates/thermo-quality-review.html +35 -35
  110. package/dist/docs/templates/thermo-review.html +35 -35
  111. package/dist/docs/templates/triage.html +35 -35
  112. package/dist/docs/troubleshooting.html +35 -35
  113. package/dist/internal/cli-deploy.d.ts.map +1 -1
  114. package/dist/internal/cli-deploy.js +9 -1
  115. package/dist/internal/deploy-client.d.ts +6 -0
  116. package/dist/internal/deploy-client.d.ts.map +1 -1
  117. package/dist/internal/deploy-client.js +3 -0
  118. package/dist/internal/discovery/agent-config.d.ts +3 -1
  119. package/dist/internal/discovery/agent-config.d.ts.map +1 -1
  120. package/dist/internal/discovery/agent-config.js +7 -4
  121. package/dist/internal/framework-storage-selection.d.ts +1 -1
  122. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  123. package/dist/internal/platform-timers.d.ts.map +1 -1
  124. package/dist/internal/platform-timers.js +20 -2
  125. package/dist/internal/reminder-runner.d.ts +79 -1
  126. package/dist/internal/reminder-runner.d.ts.map +1 -1
  127. package/dist/internal/reminder-runner.js +276 -44
  128. package/dist/internal/server.d.ts.map +1 -1
  129. package/dist/internal/server.js +7 -0
  130. package/dist/memory.d.ts +4 -0
  131. package/dist/memory.d.ts.map +1 -1
  132. package/dist/memory.js +28 -4
  133. package/dist/playground/assets/index-DLwnR9ys.css +1 -0
  134. package/dist/playground/index.html +2 -2
  135. package/dist/types.d.ts +16 -9
  136. package/dist/types.d.ts.map +1 -1
  137. package/docs/guides/slack.md +7 -0
  138. package/docs/reference/cli.md +686 -754
  139. package/package.json +1 -1
  140. package/src/channels/slack/inbound.ts +24 -0
  141. package/src/channels/slack/slack-channel.ts +123 -1
  142. package/src/internal/cli-deploy.ts +12 -1
  143. package/src/internal/deploy-client.ts +9 -0
  144. package/src/internal/discovery/agent-config.ts +8 -6
  145. package/src/internal/framework-storage-selection.ts +1 -1
  146. package/src/internal/platform-timers.ts +24 -2
  147. package/src/internal/reminder-runner.ts +426 -63
  148. package/src/internal/server.ts +8 -0
  149. package/src/memory.ts +37 -3
  150. package/src/types.ts +16 -9
  151. package/dist/docs/assets/chunks/@localSearchIndexroot.BnSgidYE.js +0 -1
  152. package/dist/docs/assets/chunks/channel.DdM5EfNW.js +0 -1
  153. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.CjfGHeg2.js +0 -1
  154. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.CjfGHeg2.js +0 -1
  155. package/dist/docs/assets/chunks/clone.wSOICb_f.js +0 -1
  156. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.Cu5X28zZ.js +0 -1
  157. package/dist/docs/assets/reference_cli.md.DLWDz9ij.js +0 -95
  158. package/dist/docs/assets/reference_cli.md.DLWDz9ij.lean.js +0 -1
  159. package/dist/playground/assets/index-C61EWMBK.css +0 -1
  160. /package/dist/playground/assets/{index-CrMWlgUU.js → index-CX6oTKwz.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cursor/july",
3
- "version": "0.1.114",
3
+ "version": "0.2.0",
4
4
  "description": "(early alpha) Filesystem-first framework for defining Cursor agents as markdown and TypeScript and serving them over channels with the Cursor SDK.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "repository": {
@@ -218,6 +218,30 @@ export function parseAssistantThreadStarted(
218
218
  };
219
219
  }
220
220
 
221
+ /**
222
+ * The receiving app's own bot user id from an Events API envelope. Slack
223
+ * stamps `authorizations[]` on every `event_callback` with the installed
224
+ * bot user the delivery is for, so a hosted guest learns its identity from
225
+ * the verified body without an `auth.test` round trip.
226
+ */
227
+ export function parseEventBodyBotUserId(body: unknown): string | undefined {
228
+ const root = asRecord(body);
229
+ if (root === null || root.type !== "event_callback") {
230
+ return undefined;
231
+ }
232
+ if (!Array.isArray(root.authorizations)) {
233
+ return undefined;
234
+ }
235
+ for (const entry of root.authorizations) {
236
+ const authorization = asRecord(entry);
237
+ const userId = asString(authorization?.user_id);
238
+ if (userId !== undefined && userId !== "") {
239
+ return userId;
240
+ }
241
+ }
242
+ return undefined;
243
+ }
244
+
221
245
  /**
222
246
  * Parse DM / IM messages. More generous than a strict channel_type check:
223
247
  * accepts `channel_type === "im"` or channel ids starting with `D`, and
@@ -25,10 +25,12 @@ import {
25
25
  } from "../../internal/continuation-identity.js";
26
26
  import { AGENT_SERVE_GENERATION_ENV_VAR } from "../../internal/cursor-relay-core.js";
27
27
  import { attachHostedAdmissionAdapter } from "../../internal/hosted-admission-adapter.js";
28
+ import type { HostedAdmissionPlatformHandoff } from "../../internal/hosted-delivery-protocol.js";
28
29
  import type {
29
30
  ArtifactsApi,
30
31
  ChannelDefinition,
31
32
  ChannelEventHandlers,
33
+ ChannelHandlerArgs,
32
34
  HostContext,
33
35
  JsonValue,
34
36
  ReceiveInput,
@@ -68,6 +70,10 @@ import {
68
70
  slackWakeChannelState,
69
71
  } from "./dispatch.js";
70
72
  import { createEventIdDedupe, type EventIdDedupe } from "./event-id-dedupe.js";
73
+ import {
74
+ parseChannelMessageEvent,
75
+ parseEventBodyBotUserId,
76
+ } from "./inbound.js";
71
77
  import {
72
78
  dispatchBlockAction,
73
79
  dispatchInteractiveFromPayload,
@@ -245,6 +251,113 @@ export function slackChannel(
245
251
  let channelWatch: ChannelPostWatcher | undefined;
246
252
  let botMentionGate: SlackBotMentionGate | undefined;
247
253
 
254
+ // Hosted admission never runs the Socket Mode branch of `onStart`, so the
255
+ // channel watch is built per delivery there. The allowlist and its `#name`
256
+ // resolution are shared across the pod; the bot's identity comes from the
257
+ // verified envelope.
258
+ let hostedAllowlist: SlackChannelAllowlist | undefined;
259
+ let hostedAllowlistReady: Promise<void> | undefined;
260
+
261
+ async function resolveHostedAllowlistNames(
262
+ allowlist: SlackChannelAllowlist
263
+ ): Promise<void> {
264
+ const names = allowlist.unresolvedNames();
265
+ if (names.length === 0) {
266
+ return;
267
+ }
268
+ try {
269
+ const botToken = await tryResolveBotToken(credentials);
270
+ if (botToken === undefined) {
271
+ getLog().error("channel watch setup degraded", {
272
+ error: "no bot token; #name entries stay unresolved",
273
+ });
274
+ return;
275
+ }
276
+ const { WebClient } = await import("@slack/web-api");
277
+ allowlist.applyResolvedNames(
278
+ await resolveChannelNames(new WebClient(botToken), names, getLog())
279
+ );
280
+ } catch (error) {
281
+ getLog().error("channel watch setup degraded", {
282
+ error: error instanceof Error ? error.message : String(error),
283
+ });
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Only a channel post needs the watcher, so mentions and DMs never wait on
289
+ * it, and a channel already matched by id never waits on `#name` resolution.
290
+ */
291
+ async function hostedChannelWatch(
292
+ payload: unknown,
293
+ args: ChannelHandlerArgs,
294
+ platformHandoff: HostedAdmissionPlatformHandoff | undefined
295
+ ): Promise<ChannelPostWatcher | undefined> {
296
+ if (channelPosts === undefined) {
297
+ return undefined;
298
+ }
299
+ const channelEvent = parseChannelMessageEvent(payload, {
300
+ includeBotAuthored: true,
301
+ });
302
+ if (channelEvent === null || channelEvent.kind === "deleted") {
303
+ return undefined;
304
+ }
305
+ hostedAllowlist ??= new SlackChannelAllowlist(channelPosts.allow);
306
+ if (
307
+ !hostedAllowlist.matches(channelEvent.message.channelId) &&
308
+ hostedAllowlist.unresolvedNames().length > 0
309
+ ) {
310
+ hostedAllowlistReady ??= resolveHostedAllowlistNames(hostedAllowlist);
311
+ await hostedAllowlistReady;
312
+ }
313
+ const botUserId = parseEventBodyBotUserId(payload);
314
+ return createChannelPostWatcher({
315
+ // Admission answers before a debounce timer could fire; edits arrive
316
+ // as their own deliveries and the edge drops them.
317
+ options: { ...channelPosts, debounceMs: 0 },
318
+ allowlist: hostedAllowlist,
319
+ getBotUserId: () => botUserId,
320
+ log: getLog(),
321
+ dispatch: (message, eventId) => {
322
+ args.waitUntil(
323
+ dispatchInboundMessage({
324
+ kind: "channel_post",
325
+ message,
326
+ handler: onChannelPost,
327
+ propagateHandlerErrors: true,
328
+ send: args.send,
329
+ credentials,
330
+ host: args.host,
331
+ artifacts: args.artifacts,
332
+ log: getLog(),
333
+ evalCommandsEnabled,
334
+ followUpStatus,
335
+ ack: reply.ack,
336
+ blockExternals,
337
+ continuation: options.continuation,
338
+ ...(args.hasContinuationSession === undefined
339
+ ? {}
340
+ : { hasContinuationSession: args.hasContinuationSession }),
341
+ ...(args.isContinuationBusy === undefined
342
+ ? {}
343
+ : { isContinuationBusy: args.isContinuationBusy }),
344
+ ...(args.getContinuationLastBotMessageTs === undefined
345
+ ? {}
346
+ : {
347
+ getContinuationLastBotMessageTs:
348
+ args.getContinuationLastBotMessageTs,
349
+ }),
350
+ ...(args.interruptContinuation === undefined
351
+ ? {}
352
+ : { interruptContinuation: args.interruptContinuation }),
353
+ ...(eventId === undefined ? {} : { eventId }),
354
+ ...(platformHandoff === undefined ? {} : { platformHandoff }),
355
+ })
356
+ );
357
+ },
358
+ });
359
+ }
360
+
248
361
  function inboundDeps(args: {
249
362
  send: SendMessageFn;
250
363
  waitUntil: (promise: Promise<unknown>) => void;
@@ -260,7 +373,10 @@ export function slackChannel(
260
373
  ) => Promise<string | undefined>;
261
374
  interruptContinuation?: (continuationToken: string) => Promise<boolean>;
262
375
  platformHandoff?: SlackInboundDispatchDeps["platformHandoff"];
376
+ /** Per-delivery watcher (hosted); the Socket Mode watcher otherwise. */
377
+ channelWatch?: ChannelPostWatcher;
263
378
  }): SlackInboundDispatchDeps {
379
+ const watch = args.channelWatch ?? channelWatch;
264
380
  return {
265
381
  send: args.send,
266
382
  continuation: options.continuation,
@@ -299,7 +415,7 @@ export function slackChannel(
299
415
  ...(args.interruptContinuation === undefined
300
416
  ? {}
301
417
  : { interruptContinuation: args.interruptContinuation }),
302
- ...(channelWatch === undefined ? {} : { channelWatch }),
418
+ ...(watch === undefined ? {} : { channelWatch: watch }),
303
419
  ...(botMentionGate === undefined ? {} : { botMentionGate }),
304
420
  suggestedPrompts: options.suggestedPrompts,
305
421
  suggestedPromptsTitle: options.suggestedPromptsTitle,
@@ -661,6 +777,11 @@ export function slackChannel(
661
777
  // would otherwise swallow the control plane's own retry of a delivery
662
778
  // that failed after the id was marked handled.
663
779
  dispatch: async ({ payload, args, platformHandoff }) => {
780
+ const hostedWatch = await hostedChannelWatch(
781
+ payload,
782
+ args,
783
+ platformHandoff
784
+ );
664
785
  const dispatched = dispatchInboundFromEventBody(
665
786
  payload,
666
787
  inboundDeps({
@@ -675,6 +796,7 @@ export function slackChannel(
675
796
  getContinuationLastBotMessageTs: args.getContinuationLastBotMessageTs,
676
797
  interruptContinuation: args.interruptContinuation,
677
798
  ...(platformHandoff === undefined ? {} : { platformHandoff }),
799
+ ...(hostedWatch === undefined ? {} : { channelWatch: hostedWatch }),
678
800
  })
679
801
  );
680
802
  return skipped(dispatched ? "not_engaged" : "not_dispatchable");
@@ -1658,11 +1658,18 @@ export async function cmdStop(
1658
1658
  }
1659
1659
  const cancellation =
1660
1660
  "cancellation" in accepted ? accepted.cancellation : undefined;
1661
+ // The backend escalates a publisher's stop of a multi-tenant app to the
1662
+ // application-level suspension; say so, and how to undo it.
1663
+ const stoppedGlobally = "scope" in accepted && accepted.scope === "global";
1661
1664
  if (ctx.noWait) {
1662
1665
  if (ctx.json) {
1663
1666
  ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
1664
1667
  } else {
1665
- ctx.out(`Stop accepted for ${slug} (status: ${accepted.status}).\n`);
1668
+ ctx.out(
1669
+ stoppedGlobally
1670
+ ? `Stop accepted for ${slug} across all tenants (status: ${accepted.status}).\n`
1671
+ : `Stop accepted for ${slug} (status: ${accepted.status}).\n`
1672
+ );
1666
1673
  }
1667
1674
  return cancellation?.failed === 0 || cancellation === undefined ? 0 : 1;
1668
1675
  }
@@ -1681,6 +1688,10 @@ export async function cmdStop(
1681
1688
  }
1682
1689
  if (ctx.json) {
1683
1690
  ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
1691
+ } else if (stoppedGlobally) {
1692
+ ctx.out(
1693
+ `Stopped ${slug} across all tenants (team ${ctx.teamId}); resume with \`${CLI} start ${slug} --global\`.\n`
1694
+ );
1684
1695
  } else {
1685
1696
  ctx.out(`Stopped ${slug} (team ${ctx.teamId}).\n`);
1686
1697
  }
@@ -295,6 +295,12 @@ export interface AgentkitStopAccepted {
295
295
  applicationId: string;
296
296
  releaseId: string;
297
297
  status: string;
298
+ /**
299
+ * `"global"` when the backend escalated the stop to the application-level
300
+ * suspension — the default for a multi-tenant app stopped by its
301
+ * publisher. Resume needs `start <slug> --global`.
302
+ */
303
+ scope?: "global";
298
304
  cancellation?: AgentkitCancelRunsResult;
299
305
  }
300
306
 
@@ -471,6 +477,9 @@ const agentkitStopAcceptedSchema = z
471
477
  applicationId: z.string().min(1),
472
478
  releaseId: z.string().min(1),
473
479
  status: z.string(),
480
+ // Tolerate scope values a newer backend may add: parse only "global",
481
+ // drop anything else rather than failing the stop response.
482
+ scope: z.literal("global").optional().catch(undefined),
474
483
  cancellation: agentkitCancelRunsResultSchema.optional(),
475
484
  })
476
485
  .passthrough();
@@ -33,16 +33,18 @@ export function runtimeArchitectureCompatibilityError(agent: {
33
33
  : undefined;
34
34
  }
35
35
 
36
+ /** Author-facing default when {@link AgentConfig.architecture} is omitted. */
37
+ export const DEFAULT_AGENT_ARCHITECTURE: AgentArchitecture = "v2";
38
+
36
39
  export function resolveArchitecture(
37
40
  ctx: DiscoveryContext,
38
41
  agentDir: string,
39
42
  architecture: AgentArchitecture | undefined
40
43
  ): AgentArchitecture | undefined {
41
- if (
42
- architecture === undefined ||
43
- architecture === "v1" ||
44
- architecture === "v2"
45
- ) {
44
+ if (architecture === undefined) {
45
+ return DEFAULT_AGENT_ARCHITECTURE;
46
+ }
47
+ if (architecture === "v1" || architecture === "v2") {
46
48
  return architecture;
47
49
  }
48
50
  ctx.diagnostics.push({
@@ -55,7 +57,7 @@ export function resolveArchitecture(
55
57
 
56
58
  /**
57
59
  * `persistent: true` selects the long-lived-pod hosting mode of v2. It has
58
- * no meaning on v1 (resident engine) or when the architecture is unset.
60
+ * no meaning on v1 (resident engine).
59
61
  */
60
62
  export function resolvePersistent(
61
63
  ctx: DiscoveryContext,
@@ -66,7 +66,7 @@ import { resolveAgentV2Hosting } from "./v2-hosting.js";
66
66
  export interface FrameworkStorageSelectionInput {
67
67
  /** `agent/storage.ts`, when the author wrote one (v1 only: discovery rejects it on v2). */
68
68
  readonly authored: StorageDefinition | undefined;
69
- /** {@link AgentConfig.architecture}; omitted means v1. */
69
+ /** {@link AgentConfig.architecture}; discovery fills `"v2"` when omitted. */
70
70
  readonly architecture?: AgentArchitecture | undefined;
71
71
  /** Environment override for tests; defaults to `process.env`. */
72
72
  readonly env?: Record<string, string | undefined>;
@@ -236,6 +236,28 @@ async function timedWorkJsonFetch(
236
236
  });
237
237
  }
238
238
 
239
+ const CP_ERROR_BODY_LIMIT = 512;
240
+
241
+ /**
242
+ * Read failures carry status and a bounded body: the guest logs them once
243
+ * per process, and a 405 body names the route the caller should be on.
244
+ */
245
+ async function reminderCpReadError(
246
+ what: string,
247
+ response: Response
248
+ ): Promise<Error> {
249
+ let body = "";
250
+ try {
251
+ body = (await response.text()).trim();
252
+ } catch {
253
+ // Status-only detail.
254
+ }
255
+ const detail = body === "" ? "" : ` ${body.slice(0, CP_ERROR_BODY_LIMIT)}`;
256
+ return new Error(
257
+ `reminder CP ${what} failed: HTTP ${response.status}${detail}`
258
+ );
259
+ }
260
+
239
261
  /**
240
262
  * Cursor-hosted HTTP ReminderControlPlane for architecture v2.
241
263
  * Speaks factory `/internal/agentsdk/reminders` (ELB → factory ECS).
@@ -275,7 +297,7 @@ export function createCursorHostedReminderControlPlane(
275
297
  return undefined;
276
298
  }
277
299
  if (!response.ok) {
278
- throw new Error(`reminder CP get failed: HTTP ${response.status}`);
300
+ throw await reminderCpReadError("get", response);
279
301
  }
280
302
  const parsed: unknown = await response.json();
281
303
  return isReminderRecord(parsed) ? parsed : undefined;
@@ -287,7 +309,7 @@ export function createCursorHostedReminderControlPlane(
287
309
  `${AGENTSDK_REMINDERS_PREFIX}/reminders`
288
310
  );
289
311
  if (!response.ok) {
290
- throw new Error(`reminder CP list failed: HTTP ${response.status}`);
312
+ throw await reminderCpReadError("list", response);
291
313
  }
292
314
  const parsed: unknown = await response.json();
293
315
  if (