@cursor/july 0.1.105 → 0.1.107

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 (217) hide show
  1. package/dist/bin/agent-serve.js +1 -8
  2. package/dist/channels/bitbucket/types.d.ts +2 -4
  3. package/dist/channels/bitbucket/types.d.ts.map +1 -1
  4. package/dist/channels/deployments/types.d.ts +5 -8
  5. package/dist/channels/deployments/types.d.ts.map +1 -1
  6. package/dist/channels/github/types.d.ts +2 -4
  7. package/dist/channels/github/types.d.ts.map +1 -1
  8. package/dist/channels/gitlab/types.d.ts +2 -5
  9. package/dist/channels/gitlab/types.d.ts.map +1 -1
  10. package/dist/channels/origin/types.d.ts +2 -3
  11. package/dist/channels/origin/types.d.ts.map +1 -1
  12. package/dist/channels/slack/defaults.d.ts.map +1 -1
  13. package/dist/channels/slack/defaults.js +3 -0
  14. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  15. package/dist/channels/slack/dispatch.js +1 -1
  16. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  17. package/dist/channels/slack/live-delivery.js +48 -11
  18. package/dist/channels/slack/types.d.ts +4 -3
  19. package/dist/channels/slack/types.d.ts.map +1 -1
  20. package/dist/docs/404.html +3 -3
  21. package/dist/docs/ab.html +6 -6
  22. package/dist/docs/assets/{app.BVwaffdK.js → app.CtuckIiL.js} +1 -1
  23. package/dist/docs/assets/chunks/@localSearchIndexroot.DXXZxiMv.js +1 -0
  24. package/dist/docs/assets/chunks/{VPLocalSearchBox.WuWTZ1x8.js → VPLocalSearchBox.Bkkhnu8K.js} +2 -2
  25. package/dist/docs/assets/chunks/{theme.BYdI_6sF.js → theme.DhpIe0Pa.js} +2 -2
  26. package/dist/docs/assets/{guides_opentelemetry.md.COh9jFVg.js → guides_opentelemetry.md.SCRMskno.js} +3 -3
  27. package/dist/docs/assets/{guides_opentelemetry.md.COh9jFVg.lean.js → guides_opentelemetry.md.SCRMskno.lean.js} +1 -1
  28. package/dist/docs/assets/{reference_channels.md.DBGcaB7m.js → reference_channels.md.D-qTqwcq.js} +2 -2
  29. package/dist/docs/assets/{reference_channels.md.DBGcaB7m.lean.js → reference_channels.md.D-qTqwcq.lean.js} +1 -1
  30. package/dist/docs/assets/{reference_cli.md.C6Lo8SEd.js → reference_cli.md.Dm67hd2D.js} +1 -1
  31. package/dist/docs/assets/{skills_otel.md.CqhobH3B.js → skills_otel.md.B5Ddv1xH.js} +1 -1
  32. package/dist/docs/assets/{style.BRuM8477.css → style.DCD_zyg3.css} +1 -1
  33. package/dist/docs/building-with-agents.html +6 -6
  34. package/dist/docs/concepts.html +5 -5
  35. package/dist/docs/deployment.html +6 -6
  36. package/dist/docs/evals.html +6 -6
  37. package/dist/docs/guides/agent-to-agent.html +6 -6
  38. package/dist/docs/guides/cloud-agents.html +6 -6
  39. package/dist/docs/guides/convert-automation.html +6 -6
  40. package/dist/docs/guides/github.html +6 -6
  41. package/dist/docs/guides/grokbot-agents.html +6 -6
  42. package/dist/docs/guides/human-in-the-loop.html +6 -6
  43. package/dist/docs/guides/mcp-oauth.html +6 -6
  44. package/dist/docs/guides/opentelemetry.html +8 -8
  45. package/dist/docs/guides/opentelemetry.md +3 -4
  46. package/dist/docs/guides/slack.html +6 -6
  47. package/dist/docs/guides/webhooks.html +6 -6
  48. package/dist/docs/hashmap.json +1 -1
  49. package/dist/docs/hillclimbing.html +6 -6
  50. package/dist/docs/index.html +6 -6
  51. package/dist/docs/llms-full.txt +22 -28
  52. package/dist/docs/quickstart.html +6 -6
  53. package/dist/docs/reference/agent-config.html +6 -6
  54. package/dist/docs/reference/artifacts.html +6 -6
  55. package/dist/docs/reference/channels.html +7 -7
  56. package/dist/docs/reference/channels.md +11 -19
  57. package/dist/docs/reference/cli.html +8 -8
  58. package/dist/docs/reference/cli.md +5 -2
  59. package/dist/docs/reference/connections.html +6 -6
  60. package/dist/docs/reference/extensions.html +6 -6
  61. package/dist/docs/reference/hooks.html +6 -6
  62. package/dist/docs/reference/http-api.html +6 -6
  63. package/dist/docs/reference/instructions.html +6 -6
  64. package/dist/docs/reference/playground.html +5 -5
  65. package/dist/docs/reference/project-layout.html +6 -6
  66. package/dist/docs/reference/prompt.html +6 -6
  67. package/dist/docs/reference/result.html +6 -6
  68. package/dist/docs/reference/schedules.html +6 -6
  69. package/dist/docs/reference/sessions.html +5 -5
  70. package/dist/docs/reference/skills.html +6 -6
  71. package/dist/docs/reference/subagents.html +6 -6
  72. package/dist/docs/reference/tools.html +6 -6
  73. package/dist/docs/scaffolding-agents.html +5 -5
  74. package/dist/docs/skills/ab.html +6 -6
  75. package/dist/docs/skills/create-agent.html +6 -6
  76. package/dist/docs/skills/debug.html +5 -5
  77. package/dist/docs/skills/evals.html +6 -6
  78. package/dist/docs/skills/framework-map.html +5 -5
  79. package/dist/docs/skills/github.html +6 -6
  80. package/dist/docs/skills/hillclimb.html +6 -6
  81. package/dist/docs/skills/index.html +5 -5
  82. package/dist/docs/skills/mcp-auth.html +6 -6
  83. package/dist/docs/skills/otel.html +8 -8
  84. package/dist/docs/skills/otel.md +3 -3
  85. package/dist/docs/skills/setup-slack.html +6 -6
  86. package/dist/docs/storage.html +6 -6
  87. package/dist/docs/templates/agentic-owners.html +6 -6
  88. package/dist/docs/templates/agents-md.html +6 -6
  89. package/dist/docs/templates/code-wiki.html +6 -6
  90. package/dist/docs/templates/demo.html +6 -6
  91. package/dist/docs/templates/grokbot-agents.html +6 -6
  92. package/dist/docs/templates/pr-autofixer.html +6 -6
  93. package/dist/docs/templates/security-help.html +6 -6
  94. package/dist/docs/templates/security-reviewer.html +6 -6
  95. package/dist/docs/templates/triage.html +6 -6
  96. package/dist/docs/troubleshooting.html +5 -5
  97. package/dist/index.d.ts +4 -5
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +3 -4
  100. package/dist/internal/cli-deploy.d.ts.map +1 -1
  101. package/dist/internal/cli-deploy.js +23 -11
  102. package/dist/internal/cursor/backend-client.d.ts +25 -0
  103. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  104. package/dist/internal/cursor/backend-client.js +57 -2
  105. package/dist/internal/deploy-client.d.ts +8 -0
  106. package/dist/internal/deploy-client.d.ts.map +1 -1
  107. package/dist/internal/deploy-client.js +14 -0
  108. package/dist/internal/deploy-manifest.d.ts +3 -0
  109. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  110. package/dist/internal/deploy-manifest.js +15 -12
  111. package/dist/internal/discovery/agent.d.ts.map +1 -1
  112. package/dist/internal/discovery/agent.js +1 -0
  113. package/dist/internal/discovery/info.d.ts.map +1 -1
  114. package/dist/internal/discovery/info.js +7 -3
  115. package/dist/internal/discovery/modules.d.ts +1 -1
  116. package/dist/internal/discovery/modules.d.ts.map +1 -1
  117. package/dist/internal/discovery/modules.js +1 -0
  118. package/dist/internal/discovery/project.d.ts.map +1 -1
  119. package/dist/internal/discovery/project.js +3 -0
  120. package/dist/internal/discovery/reminders.d.ts +7 -0
  121. package/dist/internal/discovery/reminders.d.ts.map +1 -0
  122. package/dist/internal/discovery/reminders.js +65 -0
  123. package/dist/internal/discovery/subagent.js +1 -0
  124. package/dist/internal/distribution.d.ts +5 -0
  125. package/dist/internal/distribution.d.ts.map +1 -1
  126. package/dist/internal/distribution.js +17 -0
  127. package/dist/internal/framework-storage-selection.d.ts +14 -3
  128. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  129. package/dist/internal/framework-storage-selection.js +36 -6
  130. package/dist/internal/guest-network.d.ts.map +1 -1
  131. package/dist/internal/guest-network.js +2 -0
  132. package/dist/internal/hosted-delivery-protocol.d.ts +48 -0
  133. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  134. package/dist/internal/hosted-delivery-protocol.js +60 -0
  135. package/dist/internal/hosted-delivery.d.ts +10 -0
  136. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  137. package/dist/internal/hosted-delivery.js +83 -18
  138. package/dist/internal/local-control-plane.d.ts +18 -2
  139. package/dist/internal/local-control-plane.d.ts.map +1 -1
  140. package/dist/internal/local-control-plane.js +199 -17
  141. package/dist/internal/reminder-control-plane-protocol.d.ts +187 -0
  142. package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -0
  143. package/dist/internal/reminder-control-plane-protocol.js +244 -0
  144. package/dist/internal/reminder-runner.d.ts +48 -6
  145. package/dist/internal/reminder-runner.d.ts.map +1 -1
  146. package/dist/internal/reminder-runner.js +336 -48
  147. package/dist/internal/reminder-schedule.d.ts +5 -2
  148. package/dist/internal/reminder-schedule.d.ts.map +1 -1
  149. package/dist/internal/reminder-schedule.js +8 -6
  150. package/dist/internal/reminder-store.d.ts +24 -7
  151. package/dist/internal/reminder-store.d.ts.map +1 -1
  152. package/dist/internal/reminder-store.js +6 -5
  153. package/dist/internal/request-error-status.d.ts +2 -0
  154. package/dist/internal/request-error-status.d.ts.map +1 -1
  155. package/dist/internal/request-error-status.js +22 -3
  156. package/dist/internal/scm/pr-url.d.ts +40 -0
  157. package/dist/internal/scm/pr-url.d.ts.map +1 -1
  158. package/dist/internal/scm/pr-url.js +93 -14
  159. package/dist/internal/server.d.ts.map +1 -1
  160. package/dist/internal/server.js +67 -27
  161. package/dist/internal/session-engine.d.ts +4 -0
  162. package/dist/internal/session-engine.d.ts.map +1 -1
  163. package/dist/internal/session-engine.js +5 -0
  164. package/dist/playground/assets/{index-BaQzjaNK.js → index-Bq2HpEQB.js} +23 -23
  165. package/dist/playground/assets/index-CZKKNlmb.css +1 -0
  166. package/dist/playground/index.html +2 -2
  167. package/dist/reminders.d.ts +24 -5
  168. package/dist/reminders.d.ts.map +1 -1
  169. package/dist/reminders.js +28 -5
  170. package/dist/types.d.ts +89 -20
  171. package/dist/types.d.ts.map +1 -1
  172. package/docs/guides/opentelemetry.md +3 -4
  173. package/docs/reference/channels.md +11 -19
  174. package/docs/reference/cli.md +5 -2
  175. package/package.json +3 -3
  176. package/skills/otel/SKILL.md +3 -3
  177. package/src/bin/agent-serve.ts +1 -9
  178. package/src/channels/bitbucket/types.ts +2 -4
  179. package/src/channels/deployments/types.ts +5 -8
  180. package/src/channels/github/types.ts +2 -4
  181. package/src/channels/gitlab/types.ts +2 -5
  182. package/src/channels/origin/types.ts +2 -3
  183. package/src/channels/slack/defaults.ts +3 -0
  184. package/src/channels/slack/dispatch.ts +1 -0
  185. package/src/channels/slack/live-delivery.ts +53 -12
  186. package/src/channels/slack/types.ts +4 -3
  187. package/src/index.ts +6 -3
  188. package/src/internal/cli-deploy.ts +30 -12
  189. package/src/internal/cursor/backend-client.ts +90 -2
  190. package/src/internal/deploy-client.ts +16 -0
  191. package/src/internal/deploy-manifest.ts +16 -11
  192. package/src/internal/discovery/agent.ts +1 -0
  193. package/src/internal/discovery/info.ts +6 -0
  194. package/src/internal/discovery/modules.ts +2 -4
  195. package/src/internal/discovery/project.ts +3 -0
  196. package/src/internal/discovery/reminders.ts +65 -0
  197. package/src/internal/discovery/subagent.ts +1 -0
  198. package/src/internal/distribution.ts +20 -0
  199. package/src/internal/framework-storage-selection.ts +43 -6
  200. package/src/internal/guest-network.ts +3 -0
  201. package/src/internal/hosted-delivery-protocol.ts +96 -0
  202. package/src/internal/hosted-delivery.ts +154 -27
  203. package/src/internal/local-control-plane.ts +281 -22
  204. package/src/internal/reminder-control-plane-protocol.ts +499 -0
  205. package/src/internal/reminder-runner.ts +421 -48
  206. package/src/internal/reminder-schedule.ts +11 -6
  207. package/src/internal/reminder-store.ts +27 -7
  208. package/src/internal/request-error-status.ts +27 -3
  209. package/src/internal/scm/pr-url.ts +167 -20
  210. package/src/internal/server.ts +69 -16
  211. package/src/internal/session-engine.ts +5 -0
  212. package/src/reminders.ts +35 -4
  213. package/src/types.ts +95 -20
  214. package/dist/docs/assets/chunks/@localSearchIndexroot.CJxMklAy.js +0 -1
  215. package/dist/playground/assets/index-CceqnLTX.css +0 -1
  216. /package/dist/docs/assets/{reference_cli.md.C6Lo8SEd.lean.js → reference_cli.md.Dm67hd2D.lean.js} +0 -0
  217. /package/dist/docs/assets/{skills_otel.md.CqhobH3B.lean.js → skills_otel.md.B5Ddv1xH.lean.js} +0 -0
@@ -47,7 +47,7 @@ import {
47
47
  projectInfo,
48
48
  runtimeArchitectureCompatibilityError,
49
49
  } from "./discovery.js";
50
- import { packageVersion } from "./distribution.js";
50
+ import { packageGitSha, packageVersion } from "./distribution.js";
51
51
 
52
52
  export const DEPLOY_MANIFEST_VERSION: 1 = 1;
53
53
 
@@ -62,6 +62,10 @@ const agentDeployManifestV1Schema = z.object({
62
62
  manifestVersion: z.literal(DEPLOY_MANIFEST_VERSION),
63
63
  generatedAtMs: z.number().int().nonnegative(),
64
64
  cliVersion: z.string().min(1),
65
+ cliGitSha: z
66
+ .string()
67
+ .regex(/^[0-9a-f]{40,64}$/)
68
+ .optional(),
65
69
  declarations: z.object({
66
70
  egressDomains: z.array(z.string().min(1)).max(64),
67
71
  publicRoutePaths: z.array(z.string().min(1)).max(64),
@@ -115,6 +119,8 @@ export interface AgentDeployManifestV1 {
115
119
  generatedAtMs: number;
116
120
  /** `@cursor/july` version that produced the manifest. */
117
121
  cliVersion: string;
122
+ /** Git commit for a checkout-built `@cursor/july`; absent for npm builds. */
123
+ cliGitSha?: string;
118
124
  declarations: AgentDeployManifestDeclarations;
119
125
  /**
120
126
  * Full `projectInfo` payload (see `GET /v1/info`). Opaque to the wire
@@ -129,7 +135,7 @@ export interface AgentDeployManifestV1 {
129
135
  */
130
136
  export function buildAgentDeployManifest(
131
137
  project: AgentProject,
132
- options?: { nowMs?: number; cliVersion?: string }
138
+ options?: { nowMs?: number; cliVersion?: string; cliGitSha?: string }
133
139
  ): AgentDeployManifestV1 {
134
140
  const runtimeArchitectureError = runtimeArchitectureCompatibilityError(
135
141
  project.agent
@@ -167,20 +173,19 @@ export function buildAgentDeployManifest(
167
173
  manifestVersion: DEPLOY_MANIFEST_VERSION,
168
174
  generatedAtMs: options?.nowMs ?? Date.now(),
169
175
  cliVersion: options?.cliVersion ?? packageVersion(),
176
+ cliGitSha: options?.cliGitSha ?? packageGitSha(),
170
177
  declarations: {
171
178
  egressDomains: [...(project.agent.hosting?.egressDomains ?? [])],
172
179
  publicRoutePaths,
173
- // v2 ignores authored storage; deploy manifests describe
174
- // Cursor-hosted deployments, where framework-storage-selection.ts
175
- // injects the Cursor-hosted mirror.
180
+ // Deploy manifests describe Cursor-hosted deployments. When the agent
181
+ // authors no storage, framework-storage-selection.ts injects the
182
+ // platform Cursor-hosted mirror for both v1 and v2.
176
183
  frameworkStorage:
177
- project.agent.architecture === "v2"
184
+ project.storage === undefined
178
185
  ? "cursor-hosted"
179
- : project.storage === undefined
180
- ? "none"
181
- : isCursorHostedStorage(project.storage)
182
- ? "cursor-hosted"
183
- : "other",
186
+ : isCursorHostedStorage(project.storage)
187
+ ? "cursor-hosted"
188
+ : "other",
184
189
  ...(project.agent.architecture === undefined
185
190
  ? {}
186
191
  : { architecture: project.agent.architecture }),
@@ -372,6 +372,7 @@ export async function warnUnknownSlots(
372
372
  "subagents",
373
373
  "channels",
374
374
  "schedules",
375
+ "reminders",
375
376
  "hooks",
376
377
  "ab",
377
378
  "sandbox",
@@ -151,6 +151,12 @@ export function projectInfo(project: AgentProject): AgentProjectInfo {
151
151
  cron: schedule.cron,
152
152
  kind: schedule.kind,
153
153
  })),
154
+ reminders:
155
+ (project.reminders ?? []).length === 0
156
+ ? undefined
157
+ : (project.reminders ?? []).map((reminder) => ({
158
+ name: reminder.name,
159
+ })),
154
160
  hooks: project.hooks.map((hook) => hook.name),
155
161
  abs: project.abs.map((experiment) => experiment.name),
156
162
  ...(project.abConfig === undefined && project.abs.length === 0
@@ -9,10 +9,7 @@ import { type DiscoveryContext, displayPath, errorMessage } from "./context.js";
9
9
  import { isDirectory, isModuleFile, moduleStem, sortedEntries } from "./fs.js";
10
10
  import { validateName } from "./names.js";
11
11
 
12
- type ImportableKind = Exclude<
13
- DefinitionKind,
14
- "instructions" | "eval" | "reminder"
15
- >;
12
+ type ImportableKind = Exclude<DefinitionKind, "instructions" | "eval">;
16
13
 
17
14
  /** The module files of a slot directory whose filenames are valid names. */
18
15
  export async function moduleEntries(
@@ -104,6 +101,7 @@ const DEFINE_HELPER_NAME: Record<ImportableKind, string> = {
104
101
  channel: "defineChannel",
105
102
  "http-channel": "httpChannel",
106
103
  schedule: "defineSchedule",
104
+ reminder: "defineReminder",
107
105
  hook: "defineHook",
108
106
  ab: "defineAB",
109
107
  storage: "defineStorage",
@@ -31,6 +31,7 @@ import {
31
31
  } from "./fs.js";
32
32
  import { loadHooks } from "./hooks.js";
33
33
  import { NAME_PATTERN } from "./names.js";
34
+ import { loadReminders } from "./reminders.js";
34
35
  import { loadSchedules } from "./schedules.js";
35
36
  import {
36
37
  appendArtifactTool,
@@ -236,6 +237,7 @@ export async function loadAgentProject(
236
237
  }
237
238
  const schedules = await loadSchedules(ctx, join(agentDir, "schedules"));
238
239
  schedules.push(...projectSlots.schedules);
240
+ const reminders = await loadReminders(ctx, join(agentDir, "reminders"));
239
241
  const hooks = await loadHooks(ctx, join(agentDir, "hooks"));
240
242
  hooks.push(...projectSlots.hooks);
241
243
  const abs = await loadABs(ctx, agentDir);
@@ -291,6 +293,7 @@ export async function loadAgentProject(
291
293
  httpChannel: resolvedHttpChannel,
292
294
  channels,
293
295
  schedules,
296
+ reminders,
294
297
  hooks,
295
298
  abs,
296
299
  abConfig,
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `agent/reminders/*.ts`: named durable reminder fire handlers.
3
+ */
4
+
5
+ import { basename, extname, relative } from "node:path";
6
+ import type { DiscoveredReminder, ReminderDefinition } from "../../types.js";
7
+ import { getDefinitionKind } from "../brand.js";
8
+ import { type DiscoveryContext, displayPath } from "./context.js";
9
+ import { isDirectory, isModuleFile, toPosix, walkFiles } from "./fs.js";
10
+ import { importDefault } from "./modules.js";
11
+ import { NAME_PATTERN } from "./names.js";
12
+
13
+ export async function loadReminders(
14
+ ctx: DiscoveryContext,
15
+ remindersDir: string,
16
+ onDisable?: (name: string, path: string) => void
17
+ ): Promise<DiscoveredReminder[]> {
18
+ if (!(await isDirectory(remindersDir))) {
19
+ return [];
20
+ }
21
+ const reminders: DiscoveredReminder[] = [];
22
+ for (const filePath of await walkFiles(remindersDir)) {
23
+ const rel = toPosix(relative(remindersDir, filePath));
24
+ if (!isModuleFile(basename(filePath))) {
25
+ continue;
26
+ }
27
+ const name = rel.slice(0, -extname(rel).length);
28
+ if (!NAME_PATTERN.test(name) || name.includes("__")) {
29
+ ctx.diagnostics.push({
30
+ severity: "error",
31
+ path: displayPath(ctx, filePath),
32
+ message: name.includes("__")
33
+ ? `Reminder name "${name}" may not contain "__"; it is reserved for extension contributions (\`<ns>__<name>\`).`
34
+ : `Reminder name "${name}" must match ${NAME_PATTERN}.`,
35
+ });
36
+ continue;
37
+ }
38
+ const definition = await importDefault(
39
+ ctx,
40
+ filePath,
41
+ onDisable === undefined ? "reminder" : ["reminder", "disabled"]
42
+ );
43
+ if (definition === undefined) {
44
+ continue;
45
+ }
46
+ if (getDefinitionKind(definition) === "disabled") {
47
+ onDisable?.(name, filePath);
48
+ continue;
49
+ }
50
+ const reminder = definition as ReminderDefinition;
51
+ if (typeof reminder.run !== "function") {
52
+ ctx.diagnostics.push({
53
+ severity: "error",
54
+ path: displayPath(ctx, filePath),
55
+ message: "Reminder handlers require a `run` function.",
56
+ });
57
+ continue;
58
+ }
59
+ reminders.push({
60
+ name,
61
+ definition: reminder,
62
+ });
63
+ }
64
+ return reminders;
65
+ }
@@ -121,6 +121,7 @@ async function warnUnsupportedSubagentSlots(
121
121
  ["subagents", "nested subagents"],
122
122
  ["channels", "channels"],
123
123
  ["schedules", "schedules"],
124
+ ["reminders", "reminders"],
124
125
  ["hooks", "hooks"],
125
126
  ["sandbox", "sandbox files"],
126
127
  ] as const;
@@ -13,6 +13,8 @@ import { fileURLToPath, pathToFileURL } from "node:url";
13
13
 
14
14
  /** Published npm package name (pre-launch codename for Agent SDK). */
15
15
  export const PACKAGE_NAME = "@cursor/july";
16
+ const HOSTED_WORKTREE_SHA_FILENAME = ".cursor-hosted-worktree-sha";
17
+ const GIT_COMMIT_SHA_RE = /^[0-9a-f]{40,64}$/;
16
18
 
17
19
  /**
18
20
  * Bracketed product tag for stderr / ServeOptions.logger lines
@@ -59,6 +61,24 @@ export function packageVersion(): string {
59
61
  return packageManifest().version;
60
62
  }
61
63
 
64
+ /**
65
+ * Git commit of a checkout-built framework distribution. Published npm
66
+ * packages do not carry this marker and return undefined.
67
+ */
68
+ export function packageGitSha(): string | undefined {
69
+ try {
70
+ const sha = readFileSync(
71
+ join(packageRootDir(), "dist", HOSTED_WORKTREE_SHA_FILENAME),
72
+ "utf8"
73
+ )
74
+ .trim()
75
+ .toLowerCase();
76
+ return GIT_COMMIT_SHA_RE.test(sha) ? sha : undefined;
77
+ } catch {
78
+ return undefined;
79
+ }
80
+ }
81
+
62
82
  /** Facts `agent-sdk version` / `--version` print. */
63
83
  export interface CliVersionInfo {
64
84
  /** Published npm name (`@cursor/july`). */
@@ -13,7 +13,10 @@
13
13
  * That selection is a hosting-mode question, not an authoring question.
14
14
  * Architecture v2 moves it here: `agent/storage.ts` is rejected at
15
15
  * discovery (error diagnostic, so serve and deploy fail early), and the
16
- * platform picks the default
16
+ * platform picks the default. Architecture v1 with no authored file now
17
+ * uses the same hosted-vs-self-hosted split, so deleting `agent/storage.ts`
18
+ * (required to deploy the same tree onto v2) does not drop the Cursor-hosted
19
+ * session mirror on a still-v1 pod.
17
20
  *
18
21
  * - **Cursor-managed hosting** (the deployment-generation environment
19
22
  * variable is set, same signal that registers `GET /v1/manifest`):
@@ -33,8 +36,10 @@
33
36
  * - **Self-hosted**: no mirror. `--state-root` is the durable copy, which
34
37
  * is correct when the operator owns the disk.
35
38
  *
36
- * v1 keeps the authored definition (or nothing) so existing deployments are
37
- * untouched while they run.
39
+ * v1 still honors an authored `agent/storage.ts` when present. Hosted v1
40
+ * pods with no authored file get the Cursor-hosted lazy mirror (gated on
41
+ * `AGENT_SERVE_DEPLOYMENT_ID`, the same signal the previous authored sinks
42
+ * used). Self-hosted v1 with no authored file stays disk-only.
38
43
  *
39
44
  * Local disk is not part of this decision. `<stateRoot>/sessions/<id>/`
40
45
  * already holds `session.json` and `events.ndjson` unconditionally, through
@@ -44,6 +49,10 @@
44
49
 
45
50
  import { cursorHostedFiles } from "../files-backends/cursor-hosted.js";
46
51
  import { defineStorage, type StorageDefinition } from "../storage.js";
52
+ import {
53
+ AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR,
54
+ cursorHostedStorage,
55
+ } from "../storage-backends/cursor-hosted.js";
47
56
  import { cursorHostedStorageV2 } from "../storage-backends/cursor-hosted-v2.js";
48
57
  import type { AgentArchitecture } from "../types.js";
49
58
  import { resolveApiKeySync } from "./cursor/credentials.js";
@@ -54,6 +63,13 @@ import {
54
63
  } from "./framework-file-storage.js";
55
64
  import { resolveAgentV2Hosting } from "./v2-hosting.js";
56
65
 
66
+ /**
67
+ * Hosted v2 event-chunk flush debounce. Matches the session view's live poll
68
+ * cadence; a streaming turn still caps each chunk at the coordinator's batch
69
+ * limit, so a long turn writes a few dozen chunks rather than one per event.
70
+ */
71
+ export const HOSTED_V2_EVENT_FLUSH_DEBOUNCE_MS = 2_000;
72
+
57
73
  export interface FrameworkStorageSelectionInput {
58
74
  /** `agent/storage.ts`, when the author wrote one (v1 only: discovery rejects it on v2). */
59
75
  readonly authored: StorageDefinition | undefined;
@@ -73,8 +89,21 @@ export function selectFrameworkStorageDefinition(
73
89
  input: FrameworkStorageSelectionInput
74
90
  ): StorageDefinition | undefined {
75
91
  if (input.architecture !== "v2") {
76
- // v1: the author decides, or there is no mirror.
77
- return input.authored;
92
+ if (input.authored !== undefined) {
93
+ return input.authored;
94
+ }
95
+ const env = input.env ?? process.env;
96
+ if ((env[AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR] ?? "").trim() !== "") {
97
+ // Hosted v1 pod with no authored sink: keep the Cursor-hosted
98
+ // mirror (lazy restore) so agents can delete `agent/storage.ts`
99
+ // without losing durability while they still run architecture v1.
100
+ return defineStorage({
101
+ name: "cursor-hosted (lazy restore)",
102
+ policy: { restore: "off" },
103
+ ...cursorHostedStorage(),
104
+ });
105
+ }
106
+ return undefined;
78
107
  }
79
108
  if (input.authored !== undefined) {
80
109
  // Discovery rejects agent/storage.ts on v2 before an engine is built;
@@ -97,7 +126,15 @@ export function selectFrameworkStorageDefinition(
97
126
  // backend it is per-key HTTP round trips before the listener even
98
127
  // comes up). Follow-ups restore lazily by continuation or session id;
99
128
  // GET /v1/sessions lists from the sink either way.
100
- const policy = { restore: "off" } as const;
129
+ //
130
+ // Events flush on a quiet-period debounce rather than only at turn end so
131
+ // the read-only session view and dashboard trace (which have no resident
132
+ // engine to ask) see steps mid-turn. The turn-end record still lands
133
+ // before the execution reply: drainSessionDurability forces the flush.
134
+ const policy = {
135
+ restore: "off",
136
+ debounceMs: HOSTED_V2_EVENT_FLUSH_DEBOUNCE_MS,
137
+ } as const;
101
138
  // Full chain (env → bind file → stored login): frozen engines listen
102
139
  // unsigned and bind store ids + CURSOR_API_KEY at task time, after this
103
140
  // definition is constructed.
@@ -19,6 +19,8 @@
19
19
  * with that message instead of throwing.
20
20
  */
21
21
 
22
+ import { HOSTED_GUEST_RETRY_TRANSIENT } from "./hosted-delivery-protocol.js";
23
+
22
24
  export const GUEST_NETWORK_UNAVAILABLE = "guest_network_unavailable";
23
25
  export const GUEST_CONNECT_STALLED = "guest_connect_stalled";
24
26
 
@@ -369,6 +371,7 @@ export async function guestNetworkReadinessResponse(
369
371
  JSON.stringify({
370
372
  ok: false,
371
373
  error: GUEST_NETWORK_UNAVAILABLE,
374
+ retry: HOSTED_GUEST_RETRY_TRANSIENT,
372
375
  message: error.message,
373
376
  waitMs: error.waitMs,
374
377
  probeAttempts: error.probeAttempts,
@@ -2,6 +2,43 @@ export const HOSTED_DELIVERY_PROTOCOL_VERSION = 2;
2
2
  export const HOSTED_DELIVERY_HEADER = "x-agent-sdk-hosted-delivery";
3
3
  export const HOSTED_ADMISSION_PATH = "/v1/internal/hosted-admission";
4
4
  export const HOSTED_EXECUTION_PATH = "/v1/internal/hosted-execution";
5
+ export const HOSTED_GUEST_RETRY_TRANSIENT = "transient";
6
+ export const HOSTED_GUEST_RETRY_TERMINAL = "terminal";
7
+
8
+ export type HostedGuestRetryDisposition =
9
+ | typeof HOSTED_GUEST_RETRY_TRANSIENT
10
+ | typeof HOSTED_GUEST_RETRY_TERMINAL;
11
+
12
+ export type HostedGuestError = {
13
+ ok: false;
14
+ error: string;
15
+ retry: HostedGuestRetryDisposition;
16
+ message?: string;
17
+ turnStatus?: string;
18
+ };
19
+
20
+ /**
21
+ * Hosted delivery is the only HTTP surface whose failures drive Temporal
22
+ * retries. Keep its retry disposition explicit instead of making the shared
23
+ * HTTP error helper guess from a status code.
24
+ */
25
+ export function hostedGuestErrorResponse(input: {
26
+ status: number;
27
+ error: string;
28
+ retry: HostedGuestRetryDisposition;
29
+ message?: string;
30
+ turnStatus?: string;
31
+ }): Response {
32
+ const body: HostedGuestError = {
33
+ ok: false,
34
+ error: input.error,
35
+ retry: input.retry,
36
+ message: input.message,
37
+ turnStatus: input.turnStatus,
38
+ };
39
+ return Response.json(body, { status: input.status });
40
+ }
41
+
5
42
  /**
6
43
  * Filename under the serve `--state-root` (not the per-slug SessionEngine
7
44
  * dir). Hosted freeze uses `/workspace/.agent-serve`; the Temporal host
@@ -10,6 +47,25 @@ export const HOSTED_EXECUTION_PATH = "/v1/internal/hosted-execution";
10
47
  */
11
48
  export const HOSTED_EXECUTION_DIAG_FILENAME = "hosted-execution.json";
12
49
 
50
+ /**
51
+ * Reserved hosted-admission `channelKind` for durable reminder fires.
52
+ * Bypasses authored channel admission and invokes ReminderRunner.dispatch.
53
+ * Backend builds envelopes with this constant + {@link HOSTED_REMINDER_EVENT_TYPE}.
54
+ */
55
+ export const HOSTED_REMINDER_CHANNEL_KIND = "agent-sdk:reminder";
56
+
57
+ /**
58
+ * Reserved hosted-admission `eventType` for durable reminder fires.
59
+ * Payload is {@link HostedReminderFirePayload}.
60
+ */
61
+ export const HOSTED_REMINDER_EVENT_TYPE = "agent-sdk.reminder/fire";
62
+
63
+ /** Compact reminder-fire payload on the reserved hosted-admission path. */
64
+ export type HostedReminderFirePayload = {
65
+ reminderId: string;
66
+ fireCount: number;
67
+ };
68
+
13
69
  export type HostedAdmissionManagedAutomation = {
14
70
  automationUuid: string;
15
71
  mcpServerNames: string[];
@@ -171,6 +227,46 @@ export function parseHostedExecution(
171
227
  return body as HostedExecution;
172
228
  }
173
229
 
230
+ /**
231
+ * True when the admission envelope is the reserved internal reminder fire
232
+ * path (not an authored channel event).
233
+ */
234
+ export function isHostedReminderAdmission(delivery: HostedAdmission): boolean {
235
+ return (
236
+ delivery.channelKind === HOSTED_REMINDER_CHANNEL_KIND &&
237
+ delivery.eventType === HOSTED_REMINDER_EVENT_TYPE
238
+ );
239
+ }
240
+
241
+ /**
242
+ * Parse `{ reminderId, fireCount }` from a reserved reminder admission
243
+ * payload. Rejects unknown keys and non-integer fireCount.
244
+ */
245
+ export function parseHostedReminderFirePayload(
246
+ value: unknown
247
+ ): HostedReminderFirePayload | undefined {
248
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
249
+ return undefined;
250
+ }
251
+ const body = value as Record<string, unknown>;
252
+ const keys = Object.keys(body);
253
+ if (
254
+ keys.length !== 2 ||
255
+ !Object.hasOwn(body, "reminderId") ||
256
+ !Object.hasOwn(body, "fireCount") ||
257
+ !boundedString(body.reminderId, 128) ||
258
+ typeof body.fireCount !== "number" ||
259
+ !Number.isInteger(body.fireCount) ||
260
+ body.fireCount < 0
261
+ ) {
262
+ return undefined;
263
+ }
264
+ return {
265
+ reminderId: body.reminderId,
266
+ fireCount: body.fireCount,
267
+ };
268
+ }
269
+
174
270
  export function parseHostedAdmissionOutcome(
175
271
  value: unknown
176
272
  ): HostedAdmissionOutcome | undefined {