@cursor/july 0.1.100 → 0.1.101

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 (254) hide show
  1. package/dist/bin/agent-serve.js +3 -2
  2. package/dist/channels/deployments/deployments-channel.d.ts +41 -5
  3. package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
  4. package/dist/channels/deployments/deployments-channel.js +88 -8
  5. package/dist/channels/deployments/types.d.ts +4 -3
  6. package/dist/channels/deployments/types.d.ts.map +1 -1
  7. package/dist/channels/origin/defaults.d.ts +43 -0
  8. package/dist/channels/origin/defaults.d.ts.map +1 -0
  9. package/dist/channels/origin/defaults.js +111 -0
  10. package/dist/channels/origin/index.d.ts +2 -0
  11. package/dist/channels/origin/index.d.ts.map +1 -1
  12. package/dist/channels/origin/index.js +2 -0
  13. package/dist/channels/origin/origin-channel.d.ts +2 -1
  14. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  15. package/dist/channels/origin/origin-channel.js +24 -3
  16. package/dist/channels/origin/state.d.ts +32 -0
  17. package/dist/channels/origin/state.d.ts.map +1 -0
  18. package/dist/channels/origin/state.js +23 -0
  19. package/dist/channels/origin/types.d.ts +23 -1
  20. package/dist/channels/origin/types.d.ts.map +1 -1
  21. package/dist/channels/slack/api.d.ts.map +1 -1
  22. package/dist/channels/slack/api.js +2 -1
  23. package/dist/channels/slack/code-fences.d.ts +17 -0
  24. package/dist/channels/slack/code-fences.d.ts.map +1 -0
  25. package/dist/channels/slack/code-fences.js +168 -0
  26. package/dist/channels/slack/index.d.ts +1 -0
  27. package/dist/channels/slack/index.d.ts.map +1 -1
  28. package/dist/channels/slack/index.js +1 -0
  29. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  30. package/dist/channels/slack/live-delivery.js +11 -4
  31. package/dist/connections.d.ts +18 -2
  32. package/dist/connections.d.ts.map +1 -1
  33. package/dist/connections.js +18 -2
  34. package/dist/docs/404.html +2 -2
  35. package/dist/docs/ab.html +4 -4
  36. package/dist/docs/assets/{app.DZ1e0Ycq.js → app.BASO9Kih.js} +1 -1
  37. package/dist/docs/assets/chunks/@localSearchIndexroot.DbFXINq2.js +1 -0
  38. package/dist/docs/assets/chunks/{VPLocalSearchBox.l8omwc6D.js → VPLocalSearchBox.DwkqxoE0.js} +1 -1
  39. package/dist/docs/assets/chunks/{theme.JVcD6gel.js → theme.CCil_13e.js} +2 -2
  40. package/dist/docs/assets/{reference_connections.md.BiGoBAk2.js → reference_connections.md.Di6jJAXF.js} +12 -2
  41. package/dist/docs/assets/{reference_connections.md.BiGoBAk2.lean.js → reference_connections.md.Di6jJAXF.lean.js} +1 -1
  42. package/dist/docs/assets/{reference_http-api.md.BEJx9XVj.js → reference_http-api.md.oySXBO8o.js} +1 -1
  43. package/dist/docs/building-with-agents.html +4 -4
  44. package/dist/docs/concepts.html +4 -4
  45. package/dist/docs/deployment.html +4 -4
  46. package/dist/docs/evals.html +4 -4
  47. package/dist/docs/guides/agent-to-agent.html +4 -4
  48. package/dist/docs/guides/cloud-runtime.html +4 -4
  49. package/dist/docs/guides/convert-automation.html +4 -4
  50. package/dist/docs/guides/github.html +4 -4
  51. package/dist/docs/guides/human-in-the-loop.html +4 -4
  52. package/dist/docs/guides/mcp-oauth.html +4 -4
  53. package/dist/docs/guides/opentelemetry.html +4 -4
  54. package/dist/docs/guides/slack.html +4 -4
  55. package/dist/docs/guides/webhooks.html +4 -4
  56. package/dist/docs/hashmap.json +1 -1
  57. package/dist/docs/hillclimbing.html +4 -4
  58. package/dist/docs/index.html +4 -4
  59. package/dist/docs/llms-full.txt +32 -1
  60. package/dist/docs/quickstart.html +4 -4
  61. package/dist/docs/reference/agent-config.html +4 -4
  62. package/dist/docs/reference/artifacts.html +4 -4
  63. package/dist/docs/reference/channels.html +4 -4
  64. package/dist/docs/reference/cli.html +4 -4
  65. package/dist/docs/reference/connections.html +16 -6
  66. package/dist/docs/reference/connections.md +31 -0
  67. package/dist/docs/reference/hooks.html +4 -4
  68. package/dist/docs/reference/http-api.html +6 -6
  69. package/dist/docs/reference/http-api.md +1 -1
  70. package/dist/docs/reference/instructions.html +4 -4
  71. package/dist/docs/reference/playground.html +4 -4
  72. package/dist/docs/reference/project-layout.html +4 -4
  73. package/dist/docs/reference/prompt.html +4 -4
  74. package/dist/docs/reference/schedules.html +4 -4
  75. package/dist/docs/reference/sessions.html +4 -4
  76. package/dist/docs/reference/skills.html +4 -4
  77. package/dist/docs/reference/subagents.html +4 -4
  78. package/dist/docs/reference/tools.html +4 -4
  79. package/dist/docs/scaffolding-agents.html +4 -4
  80. package/dist/docs/skills/ab.html +4 -4
  81. package/dist/docs/skills/create-agent.html +4 -4
  82. package/dist/docs/skills/debug.html +4 -4
  83. package/dist/docs/skills/evals.html +4 -4
  84. package/dist/docs/skills/framework-map.html +4 -4
  85. package/dist/docs/skills/github.html +4 -4
  86. package/dist/docs/skills/hillclimb.html +4 -4
  87. package/dist/docs/skills/index.html +4 -4
  88. package/dist/docs/skills/mcp-auth.html +4 -4
  89. package/dist/docs/skills/otel.html +4 -4
  90. package/dist/docs/skills/setup-slack.html +4 -4
  91. package/dist/docs/storage.html +4 -4
  92. package/dist/docs/templates/agentic-owners.html +4 -4
  93. package/dist/docs/templates/agents-md.html +4 -4
  94. package/dist/docs/templates/code-wiki.html +4 -4
  95. package/dist/docs/templates/demo.html +4 -4
  96. package/dist/docs/templates/pr-autofixer.html +4 -4
  97. package/dist/docs/templates/security-help.html +4 -4
  98. package/dist/docs/templates/security-reviewer.html +4 -4
  99. package/dist/docs/templates/triage.html +4 -4
  100. package/dist/docs/troubleshooting.html +4 -4
  101. package/dist/extensions/cursor-cloud-agents/extension.d.ts +55 -0
  102. package/dist/extensions/cursor-cloud-agents/extension.d.ts.map +1 -0
  103. package/dist/extensions/cursor-cloud-agents/extension.js +64 -0
  104. package/dist/extensions/cursor-cloud-agents/lib/cloud.d.ts +30 -0
  105. package/dist/extensions/cursor-cloud-agents/lib/cloud.d.ts.map +1 -0
  106. package/dist/extensions/cursor-cloud-agents/lib/cloud.js +45 -0
  107. package/dist/extensions/cursor-cloud-agents/lib/fleet.d.ts +25 -0
  108. package/dist/extensions/cursor-cloud-agents/lib/fleet.d.ts.map +1 -0
  109. package/dist/extensions/cursor-cloud-agents/lib/fleet.js +85 -0
  110. package/dist/extensions/cursor-cloud-agents/lib/repos.d.ts +15 -0
  111. package/dist/extensions/cursor-cloud-agents/lib/repos.d.ts.map +1 -0
  112. package/dist/extensions/cursor-cloud-agents/lib/repos.js +55 -0
  113. package/dist/extensions/cursor-cloud-agents/skills/handoff.md +27 -0
  114. package/dist/extensions/cursor-cloud-agents/tools/follow_up.d.ts +4 -0
  115. package/dist/extensions/cursor-cloud-agents/tools/follow_up.d.ts.map +1 -0
  116. package/dist/extensions/cursor-cloud-agents/tools/follow_up.js +37 -0
  117. package/dist/extensions/cursor-cloud-agents/tools/launch.d.ts +4 -0
  118. package/dist/extensions/cursor-cloud-agents/tools/launch.d.ts.map +1 -0
  119. package/dist/extensions/cursor-cloud-agents/tools/launch.js +83 -0
  120. package/dist/extensions/cursor-cloud-agents/tools/list.d.ts +4 -0
  121. package/dist/extensions/cursor-cloud-agents/tools/list.d.ts.map +1 -0
  122. package/dist/extensions/cursor-cloud-agents/tools/list.js +36 -0
  123. package/dist/extensions/cursor-cloud-agents/tools/status.d.ts +4 -0
  124. package/dist/extensions/cursor-cloud-agents/tools/status.d.ts.map +1 -0
  125. package/dist/extensions/cursor-cloud-agents/tools/status.js +41 -0
  126. package/dist/extensions.d.ts +49 -0
  127. package/dist/extensions.d.ts.map +1 -0
  128. package/dist/extensions.js +75 -0
  129. package/dist/index.d.ts +2 -1
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +2 -1
  132. package/dist/internal/advertise-tools.d.ts +4 -1
  133. package/dist/internal/advertise-tools.d.ts.map +1 -1
  134. package/dist/internal/advertise-tools.js +20 -1
  135. package/dist/internal/as-of.d.ts +18 -0
  136. package/dist/internal/as-of.d.ts.map +1 -1
  137. package/dist/internal/as-of.js +131 -0
  138. package/dist/internal/cli-deploy.d.ts.map +1 -1
  139. package/dist/internal/cli-deploy.js +64 -19
  140. package/dist/internal/continuation-identity.d.ts.map +1 -1
  141. package/dist/internal/continuation-identity.js +3 -1
  142. package/dist/internal/cursor/hosted-deployment-secrets.d.ts +16 -0
  143. package/dist/internal/cursor/hosted-deployment-secrets.d.ts.map +1 -0
  144. package/dist/internal/cursor/hosted-deployment-secrets.js +61 -0
  145. package/dist/internal/cursor-deployment-relay.d.ts +15 -1
  146. package/dist/internal/cursor-deployment-relay.d.ts.map +1 -1
  147. package/dist/internal/cursor-deployment-relay.js +46 -7
  148. package/dist/internal/deploy-client.d.ts +23 -0
  149. package/dist/internal/deploy-client.d.ts.map +1 -1
  150. package/dist/internal/deploy-client.js +54 -12
  151. package/dist/internal/discovery.d.ts.map +1 -1
  152. package/dist/internal/discovery.js +415 -120
  153. package/dist/internal/distribution.d.ts.map +1 -1
  154. package/dist/internal/distribution.js +5 -0
  155. package/dist/internal/framework-file-storage.d.ts +3 -3
  156. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  157. package/dist/internal/framework-file-storage.js +34 -5
  158. package/dist/internal/hosted-delivery.d.ts +3 -1
  159. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  160. package/dist/internal/hosted-delivery.js +19 -32
  161. package/dist/internal/hosted-execution-diag.d.ts +5 -0
  162. package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
  163. package/dist/internal/hosted-execution-diag.js +5 -0
  164. package/dist/internal/hosted-execution-flush.d.ts +46 -0
  165. package/dist/internal/hosted-execution-flush.d.ts.map +1 -0
  166. package/dist/internal/hosted-execution-flush.js +81 -0
  167. package/dist/internal/mcp-endpoint.js +1 -0
  168. package/dist/internal/mcp-host.d.ts +2 -1
  169. package/dist/internal/mcp-host.d.ts.map +1 -1
  170. package/dist/internal/mcp-host.js +35 -15
  171. package/dist/internal/review-comments.d.ts +1 -1
  172. package/dist/internal/review-comments.d.ts.map +1 -1
  173. package/dist/internal/review-comments.js +4 -4
  174. package/dist/internal/server.d.ts +3 -0
  175. package/dist/internal/server.d.ts.map +1 -1
  176. package/dist/internal/server.js +79 -104
  177. package/dist/internal/session-engine.d.ts +9 -7
  178. package/dist/internal/session-engine.d.ts.map +1 -1
  179. package/dist/internal/session-engine.js +45 -34
  180. package/dist/internal/session-run-log.d.ts +2 -1
  181. package/dist/internal/session-run-log.d.ts.map +1 -1
  182. package/dist/internal/session-run-log.js +2 -1
  183. package/dist/internal/start-log-store.d.ts +60 -0
  184. package/dist/internal/start-log-store.d.ts.map +1 -0
  185. package/dist/internal/start-log-store.js +151 -0
  186. package/dist/internal/tool-policy.d.ts +8 -0
  187. package/dist/internal/tool-policy.d.ts.map +1 -1
  188. package/dist/internal/tool-policy.js +19 -7
  189. package/dist/playground/assets/{index-DVs98vPL.js → index-BnNN1-EK.js} +45 -45
  190. package/dist/playground/assets/index-DBQCwFp0.css +1 -0
  191. package/dist/playground/index.html +2 -2
  192. package/dist/storage-protocol.d.ts +24 -0
  193. package/dist/storage-protocol.d.ts.map +1 -1
  194. package/dist/storage-protocol.js +33 -0
  195. package/dist/storage.d.ts +1 -1
  196. package/dist/storage.d.ts.map +1 -1
  197. package/dist/storage.js +1 -1
  198. package/dist/types.d.ts +118 -6
  199. package/dist/types.d.ts.map +1 -1
  200. package/dist/types.js +29 -1
  201. package/docs/reference/connections.md +31 -0
  202. package/docs/reference/http-api.md +1 -1
  203. package/package.json +18 -4
  204. package/src/bin/agent-serve.ts +11 -2
  205. package/src/channels/deployments/deployments-channel.ts +117 -8
  206. package/src/channels/deployments/types.ts +4 -3
  207. package/src/channels/origin/defaults.ts +143 -0
  208. package/src/channels/origin/index.ts +2 -0
  209. package/src/channels/origin/origin-channel.ts +37 -1
  210. package/src/channels/origin/state.ts +54 -0
  211. package/src/channels/origin/types.ts +24 -0
  212. package/src/channels/slack/api.ts +2 -1
  213. package/src/channels/slack/code-fences.ts +183 -0
  214. package/src/channels/slack/index.ts +1 -0
  215. package/src/channels/slack/live-delivery.ts +16 -13
  216. package/src/connections.ts +18 -1
  217. package/src/extensions/cursor-cloud-agents/extension.ts +118 -0
  218. package/src/extensions/cursor-cloud-agents/lib/cloud.ts +79 -0
  219. package/src/extensions/cursor-cloud-agents/lib/fleet.ts +109 -0
  220. package/src/extensions/cursor-cloud-agents/lib/repos.ts +81 -0
  221. package/src/extensions/cursor-cloud-agents/skills/handoff.md +27 -0
  222. package/src/extensions/cursor-cloud-agents/tools/follow_up.ts +29 -0
  223. package/src/extensions/cursor-cloud-agents/tools/launch.ts +84 -0
  224. package/src/extensions/cursor-cloud-agents/tools/list.ts +31 -0
  225. package/src/extensions/cursor-cloud-agents/tools/status.ts +39 -0
  226. package/src/extensions.ts +98 -0
  227. package/src/index.ts +2 -0
  228. package/src/internal/advertise-tools.ts +37 -12
  229. package/src/internal/as-of.ts +163 -0
  230. package/src/internal/cli-deploy.ts +86 -23
  231. package/src/internal/continuation-identity.ts +3 -1
  232. package/src/internal/cursor/hosted-deployment-secrets.ts +72 -0
  233. package/src/internal/cursor-deployment-relay.ts +65 -8
  234. package/src/internal/deploy-client.ts +73 -18
  235. package/src/internal/discovery.ts +470 -113
  236. package/src/internal/distribution.ts +5 -0
  237. package/src/internal/framework-file-storage.ts +39 -5
  238. package/src/internal/hosted-delivery.ts +38 -63
  239. package/src/internal/hosted-execution-diag.ts +6 -0
  240. package/src/internal/hosted-execution-flush.ts +142 -0
  241. package/src/internal/mcp-endpoint.ts +1 -0
  242. package/src/internal/mcp-host.ts +40 -14
  243. package/src/internal/review-comments.ts +5 -2
  244. package/src/internal/server.ts +81 -100
  245. package/src/internal/session-engine.ts +56 -28
  246. package/src/internal/session-run-log.ts +2 -1
  247. package/src/internal/start-log-store.ts +177 -0
  248. package/src/internal/tool-policy.ts +39 -9
  249. package/src/storage-protocol.ts +37 -0
  250. package/src/storage.ts +1 -0
  251. package/src/types.ts +172 -14
  252. package/dist/docs/assets/chunks/@localSearchIndexroot.j70vvPL4.js +0 -1
  253. package/dist/playground/assets/index-BszoQDc6.css +0 -1
  254. /package/dist/docs/assets/{reference_http-api.md.BEJx9XVj.lean.js → reference_http-api.md.oySXBO8o.lean.js} +0 -0
@@ -72,6 +72,7 @@ import { CursorGithubCredentialProvider } from "./cursor/github-credentials.js";
72
72
  import {
73
73
  DeploymentEventRelay,
74
74
  fanOutDeploymentEvent,
75
+ mergeResolvedSubscriptions,
75
76
  type ResolvedSubscription,
76
77
  resolveDeploymentSubscriptions,
77
78
  } from "./cursor-deployment-relay.js";
@@ -111,6 +112,7 @@ import {
111
112
  import { createHostPlatforms, warmupHostGitHubAuth } from "./host-platforms.js";
112
113
  import { registerHostedDeliveryRoute } from "./hosted-delivery.js";
113
114
  import { hostedErrorStackSnippet } from "./hosted-execution-diag.js";
115
+ import { flushHostedExecutionProcessDeath } from "./hosted-execution-flush.js";
114
116
  import {
115
117
  buildHttpChannelRoutes,
116
118
  errorResponse,
@@ -194,6 +196,7 @@ import {
194
196
  unregisterSessionRunFatalFlush,
195
197
  } from "./session-run-log.js";
196
198
  import { sessionUrlsForRequest } from "./session-urls.js";
199
+ import { flushLocalStartLog, selectStartLogStore } from "./start-log-store.js";
197
200
  import { catalogDetailEntry, catalogListEntry } from "./tool-catalog.js";
198
201
 
199
202
  function memoryHealthFields(engines: readonly SessionEngine[]) {
@@ -256,6 +259,8 @@ export interface StartServerInternalOptions {
256
259
  mcpOauthSecrets?: McpOauthSecretsTarget;
257
260
  /** Test seam: persist hosted-execution run logs without a live store. */
258
261
  sessionRunLog?: ReturnType<typeof selectSessionRunLogStore>;
262
+ /** Test seam: persist the engine start.log tee without a live store. */
263
+ startLogStore?: ReturnType<typeof selectStartLogStore>;
259
264
  }
260
265
 
261
266
  /**
@@ -550,10 +555,35 @@ export async function startServer(
550
555
  snapshotLogs: () => logRing.since(),
551
556
  })
552
557
  : undefined);
558
+ const startLogStore =
559
+ options.startLogStore ??
560
+ (mounts.length === 1 && mounts[0] !== undefined
561
+ ? selectStartLogStore({
562
+ project: mounts[0].project,
563
+ logger,
564
+ })
565
+ : undefined);
566
+ // The running claim stamps this before the turn; process-death flush
567
+ // reuses it so the tee snapshot lands under the same ses_ key.
568
+ let lastStartLogId: string | undefined;
569
+ const flushStartLog = async (startLogId: string): Promise<void> => {
570
+ lastStartLogId = startLogId;
571
+ if (startLogStore === undefined) {
572
+ return;
573
+ }
574
+ await flushLocalStartLog({ store: startLogStore, startLogId });
575
+ };
553
576
  if (sessionRunLog !== undefined) {
554
- registerSessionRunFatalFlush((error) =>
555
- sessionRunLog.flushProcessDeath("uncaught_exception", error)
556
- );
577
+ registerSessionRunFatalFlush(async (error) => {
578
+ await flushHostedExecutionProcessDeath({
579
+ flushProcessDeath: (reason, flushError) =>
580
+ sessionRunLog.flushProcessDeath(reason, flushError),
581
+ flushStartLog,
582
+ startLogId: lastStartLogId,
583
+ reason: "uncaught_exception",
584
+ error,
585
+ });
586
+ });
557
587
  }
558
588
  const sdkRunner =
559
589
  options.runner ??
@@ -770,6 +800,7 @@ export async function startServer(
770
800
  logRing,
771
801
  diagStateRoot: baseStateRoot,
772
802
  sessionRunLog,
803
+ flushStartLog,
773
804
  ...(options.authToken === undefined
774
805
  ? {}
775
806
  : { authToken: options.authToken }),
@@ -1224,7 +1255,13 @@ export async function startServer(
1224
1255
  },
1225
1256
  close: async () => {
1226
1257
  if (sessionRunLog !== undefined) {
1227
- await sessionRunLog.flushProcessDeath("process_exit");
1258
+ await flushHostedExecutionProcessDeath({
1259
+ flushProcessDeath: (reason) =>
1260
+ sessionRunLog.flushProcessDeath(reason),
1261
+ flushStartLog,
1262
+ startLogId: lastStartLogId,
1263
+ reason: "process_exit",
1264
+ });
1228
1265
  unregisterSessionRunFatalFlush();
1229
1266
  }
1230
1267
  playgroundDev?.stop();
@@ -1282,6 +1319,7 @@ function mountAgentRoutes(
1282
1319
  logger: (line: string) => void;
1283
1320
  logRing: LogRing;
1284
1321
  sessionRunLog?: ReturnType<typeof selectSessionRunLogStore>;
1322
+ flushStartLog?: (startLogId: string) => Promise<void>;
1285
1323
  authToken?: string;
1286
1324
  allowAnonymous?: boolean;
1287
1325
  /** Cursor account bridge + this mount's account MCP connections, when authored. */
@@ -1303,6 +1341,7 @@ function mountAgentRoutes(
1303
1341
  logger,
1304
1342
  logRing,
1305
1343
  sessionRunLog,
1344
+ flushStartLog,
1306
1345
  } = args;
1307
1346
  // "" (single) -> "/v1/..."; "my-agent" (multi) -> "/my-agent/v1/...".
1308
1347
  const base = slug === "" ? "" : `/${slug}`;
@@ -1366,7 +1405,12 @@ function mountAgentRoutes(
1366
1405
  : { stateRoot: args.diagStateRoot }),
1367
1406
  ...(sessionRunLog === undefined
1368
1407
  ? {}
1369
- : { persistSessionRun: (document) => sessionRunLog.persist(document) }),
1408
+ : {
1409
+ persistSessionRun: async (document) => {
1410
+ await sessionRunLog.persist(document);
1411
+ },
1412
+ flushStartLog,
1413
+ }),
1370
1414
  snapshotLogs: () => logRing.since(),
1371
1415
  });
1372
1416
  }
@@ -1474,51 +1518,6 @@ function mountAgentRoutes(
1474
1518
  },
1475
1519
  });
1476
1520
 
1477
- // v2 only: one session-level hosted-execution log on the framework
1478
- // store. Distinct from GET /v1/logs (process-wide ring). Keyed by the
1479
- // factory session public id so an execute_throw that never minted a
1480
- // July session is still readable after the pod parks.
1481
- if (isV2Project(project) && sessionRunLog !== undefined) {
1482
- router.add({
1483
- method: "GET",
1484
- pattern: `${base}/v1/session/:sessionId/runs`,
1485
- auth: httpAuth,
1486
- handler: async (_request, ctx) => {
1487
- const sessionId = ctx.params.sessionId ?? "";
1488
- if (sessionId.length === 0 || sessionId.includes("/")) {
1489
- return errorResponse(
1490
- 400,
1491
- "invalid_request",
1492
- "Expected a session id."
1493
- );
1494
- }
1495
- const record = await engine.getOrRestoreSession(sessionId);
1496
- if (record === undefined) {
1497
- return errorResponse(
1498
- 404,
1499
- "session_not_found",
1500
- `Unknown session ${sessionId}.`
1501
- );
1502
- }
1503
- const ownership = requireSessionOwner(record, ctx.auth, {
1504
- relax: relaxSessionReadAuth,
1505
- beyondLoopback: relaxSessionReadAuthBeyondLoopback,
1506
- requestIp: ctx.requestIp,
1507
- });
1508
- if (ownership !== undefined) {
1509
- return ownership;
1510
- }
1511
- const document = await sessionRunLog.read(sessionId);
1512
- return jsonResponse({
1513
- sessionId,
1514
- updatedAt: document?.updatedAt,
1515
- lastFailure: document?.lastFailure ?? null,
1516
- executions: document?.executions ?? [],
1517
- });
1518
- },
1519
- });
1520
- }
1521
-
1522
1521
  router.add({
1523
1522
  method: "GET",
1524
1523
  pattern: `${base}/v1/sessions`,
@@ -2694,13 +2693,11 @@ interface CursorDeploymentTarget {
2694
2693
  runtime: MountedRuntime;
2695
2694
  channelId: string;
2696
2695
  options: DeploymentsChannelOptions;
2697
- subscriptions: ResolvedSubscription[];
2698
2696
  }
2699
2697
 
2700
- /** Every mounted `deploymentsChannel`, with its resolved subscriptions. */
2698
+ /** Every mounted `deploymentsChannel`. Subscriptions are re-resolved live. */
2701
2699
  function collectCursorDeploymentTargets(
2702
- runtimes: MountedRuntime[],
2703
- skipped: string[]
2700
+ runtimes: MountedRuntime[]
2704
2701
  ): CursorDeploymentTarget[] {
2705
2702
  const targets: CursorDeploymentTarget[] = [];
2706
2703
  for (const runtime of runtimes) {
@@ -2709,19 +2706,24 @@ function collectCursorDeploymentTargets(
2709
2706
  if (options === undefined) {
2710
2707
  continue;
2711
2708
  }
2712
- const subscriptions = resolveDeploymentSubscriptions(options);
2713
- if (subscriptions.length === 0) {
2714
- // Declared the channel but named no deploy sources — usually an unset
2715
- // env var. Say so rather than starting a relay that receives nothing.
2716
- skipped.push(`${runtime.slug || runtime.project.name}/${channel.id}`);
2717
- continue;
2718
- }
2719
- targets.push({ runtime, channelId: channel.id, options, subscriptions });
2709
+ targets.push({
2710
+ runtime,
2711
+ channelId: channel.id,
2712
+ options,
2713
+ });
2720
2714
  }
2721
2715
  }
2722
2716
  return targets;
2723
2717
  }
2724
2718
 
2719
+ function liveDeploymentSubscriptionUnion(
2720
+ targets: readonly CursorDeploymentTarget[]
2721
+ ): ResolvedSubscription[] {
2722
+ return mergeResolvedSubscriptions(
2723
+ targets.map((target) => resolveDeploymentSubscriptions(target.options))
2724
+ );
2725
+ }
2726
+
2725
2727
  /**
2726
2728
  * Cursor deployment-events relay: automatic when a mounted agent authored
2727
2729
  * `deploymentsChannel({ ... })`. One relay per process reads
@@ -2736,13 +2738,7 @@ function startCursorDeploymentRelay(input: {
2736
2738
  logger: (line: string) => void;
2737
2739
  }): DeploymentEventRelay | undefined {
2738
2740
  const { options, runtimes, accountKey, logger } = input;
2739
- const skipped: string[] = [];
2740
- const targets = collectCursorDeploymentTargets(runtimes, skipped);
2741
- if (skipped.length > 0) {
2742
- logger(
2743
- `[agent-sdk] deployment-events: no deploy sources declared on ${skipped.join(", ")}; those channels receive nothing`
2744
- );
2745
- }
2741
+ const targets = collectCursorDeploymentTargets(runtimes);
2746
2742
  if (targets.length === 0) {
2747
2743
  return undefined;
2748
2744
  }
@@ -2757,34 +2753,19 @@ function startCursorDeploymentRelay(input: {
2757
2753
  `This project has channels subscribed to deployment events (deploymentsChannel({ deploySourceUris })) but the host is not signed in to Cursor. ${SIGN_IN_HINT}`
2758
2754
  );
2759
2755
  }
2760
- // Union across channels: one registration per deploy source for the whole
2761
- // process, however many channels named it. Per-channel environment and
2762
- // event-type filtering happens at dispatch.
2763
- const bySource = new Map<string, ResolvedSubscription>();
2764
- for (const target of targets) {
2765
- for (const subscription of target.subscriptions) {
2766
- const existing = bySource.get(subscription.deploySourceUri);
2767
- if (existing === undefined) {
2768
- bySource.set(subscription.deploySourceUri, {
2769
- deploySourceUri: subscription.deploySourceUri,
2770
- environments: [...subscription.environments],
2771
- events: new Set(subscription.events),
2772
- });
2773
- continue;
2774
- }
2775
- for (const event of subscription.events) existing.events.add(event);
2776
- // An empty environment list means "every environment", so it absorbs.
2777
- existing.environments =
2778
- existing.environments.length === 0 ||
2779
- subscription.environments.length === 0
2780
- ? []
2781
- : [
2782
- ...new Set([
2783
- ...existing.environments,
2784
- ...subscription.environments,
2785
- ]),
2786
- ];
2787
- }
2756
+ // Union is re-resolved on each heartbeat and fan-out so a channel that
2757
+ // refreshes `deploySourceUris` after start (Change Monitor lists the
2758
+ // team's sources every 10 minutes) registers the new URIs.
2759
+ const initial = liveDeploymentSubscriptionUnion(targets);
2760
+ if (initial.length === 0) {
2761
+ logger(
2762
+ `[agent-sdk] deployment-events: no deploy sources yet on ${targets
2763
+ .map(
2764
+ (target) =>
2765
+ `${target.runtime.slug || target.runtime.project.name}/${target.channelId}`
2766
+ )
2767
+ .join(", ")}; the relay will register when a channel lists some`
2768
+ );
2788
2769
  }
2789
2770
  // Same host split as scm-events: `/v0/deployment-events/*` is on
2790
2771
  // api.cursor.com, not api2.
@@ -2795,7 +2776,7 @@ function startCursorDeploymentRelay(input: {
2795
2776
  baseUrl: cursorExternalApiUrl(),
2796
2777
  apiKey: accountKey.apiKey,
2797
2778
  stateDir: join(baseStateRoot, "deployment-events"),
2798
- subscriptions: [...bySource.values()],
2779
+ subscriptions: () => liveDeploymentSubscriptionUnion(targets),
2799
2780
  logger,
2800
2781
  dispatch: async (event) =>
2801
2782
  fanOutDeploymentEvent({
@@ -2803,7 +2784,7 @@ function startCursorDeploymentRelay(input: {
2803
2784
  logger,
2804
2785
  targets: targets.map((target) => ({
2805
2786
  name: `${target.runtime.slug || target.runtime.project.name}/${target.channelId}`,
2806
- subscriptions: target.subscriptions,
2787
+ subscriptions: resolveDeploymentSubscriptions(target.options),
2807
2788
  deliver: (delivered) =>
2808
2789
  dispatchDeploymentEvent({
2809
2790
  options: target.options,
@@ -2818,7 +2799,7 @@ function startCursorDeploymentRelay(input: {
2818
2799
  });
2819
2800
  relay.start();
2820
2801
  logger(
2821
- `[agent-sdk] deployment-events: relaying ${bySource.size} deploy-source subscription(s) to ${targets.length} channel(s)`
2802
+ `[agent-sdk] deployment-events: relaying ${initial.length} deploy-source subscription(s) to ${targets.length} channel(s)`
2822
2803
  );
2823
2804
  return relay;
2824
2805
  }
@@ -50,16 +50,17 @@ import {
50
50
  type ConnectionTransport,
51
51
  DEFAULT_MODEL,
52
52
  DIRECT_TOOL_CALL_CHANNEL_ID,
53
+ type DiscoveredConnection,
53
54
  type DiscoveredTool,
54
55
  type HookHandler,
55
56
  type HostContext,
56
57
  type HostMcpRegistry,
58
+ type HostMcpToolInfo,
57
59
  type HttpChannelDefinition,
58
60
  isCursorAccountTransport,
59
61
  isHostOauthConnectionTransport,
60
62
  isSymbolicConnectionTransport,
61
63
  type JsonValue,
62
- type McpToolAnnotations,
63
64
  type PendingApproval,
64
65
  type PendingAuth,
65
66
  type ReceiveInput,
@@ -89,7 +90,12 @@ import {
89
90
  } from "./advertise-tools.js";
90
91
  import { ApprovalGate } from "./approval-gate.js";
91
92
  import { ArtifactsStore } from "./artifacts-store.js";
92
- import { assertTimeArgs, normalizeAsOf, sessionClock } from "./as-of.js";
93
+ import {
94
+ assertTimeArgs,
95
+ normalizeAsOf,
96
+ schemaTimeWindowKeys,
97
+ sessionClock,
98
+ } from "./as-of.js";
93
99
  import { SessionAuthError, samePrincipal } from "./auth.js";
94
100
  import { mergeCloudOptions, resolveSessionRuntime } from "./cloud-merge.js";
95
101
  import {
@@ -600,18 +606,25 @@ export class SessionEngine {
600
606
  setResolvedConnections(connections: EngineResolvedConnection[]): void {
601
607
  this.resolvedConnections = connections;
602
608
  for (const connection of connections) {
609
+ const authored = this.authoredConnection(connection.name);
603
610
  this.mcpHost.addConnection({
611
+ ...(authored ?? { name: connection.name }),
604
612
  name: connection.name,
605
613
  transport: {
606
614
  url: connection.localUrl,
607
- ...(connection.headers === undefined
608
- ? {}
609
- : { headers: connection.headers }),
615
+ headers: connection.headers,
610
616
  },
611
617
  });
612
618
  }
613
619
  }
614
620
 
621
+ private authoredConnection(name: string): DiscoveredConnection | undefined {
622
+ return (
623
+ this.project.agent.connections.find((item) => item.name === name) ??
624
+ this.project.agent.hostConnections?.find((item) => item.name === name)
625
+ );
626
+ }
627
+
615
628
  setCloudServerToolsConnection(connection: ConnectionTransport): void {
616
629
  this.cloudServerToolsConnection = connection;
617
630
  }
@@ -2753,6 +2766,9 @@ export class SessionEngine {
2753
2766
  input: validation.value,
2754
2767
  asOf: args.record.asOf,
2755
2768
  timeArgs: args.tool.definition.timeArgs,
2769
+ ...(args.tool.advertised === true && args.record.asOf !== undefined
2770
+ ? { timeWindow: schemaTimeWindowKeys(args.tool.inputSchema) }
2771
+ : {}),
2756
2772
  });
2757
2773
  if (decision.kind === "answer") {
2758
2774
  this.stubbedCallIds.get(args.record.sessionId)?.add(toolCallId);
@@ -3083,13 +3099,6 @@ export class SessionEngine {
3083
3099
  await this.oauthHub.persistFor(connection, authored.transport.url);
3084
3100
  }
3085
3101
 
3086
- private authoredConnection(name: string) {
3087
- return (
3088
- this.project.agent.connections.find((entry) => entry.name === name) ??
3089
- this.project.agent.hostConnections?.find((entry) => entry.name === name)
3090
- );
3091
- }
3092
-
3093
3102
  private async mintAuthUrl(
3094
3103
  connection: string,
3095
3104
  sessionId: string
@@ -3632,6 +3641,9 @@ export class SessionEngine {
3632
3641
  input: validation.value,
3633
3642
  asOf: record?.asOf,
3634
3643
  timeArgs: tool.definition.timeArgs,
3644
+ ...(tool.advertised === true && record?.asOf !== undefined
3645
+ ? { timeWindow: schemaTimeWindowKeys(tool.inputSchema) }
3646
+ : {}),
3635
3647
  });
3636
3648
 
3637
3649
  const started = Date.now();
@@ -3770,28 +3782,42 @@ export class SessionEngine {
3770
3782
  }
3771
3783
 
3772
3784
  /**
3773
- * Whether a dry run answers a bridged MCP call, for the cloud host bridge —
3785
+ * Whether the host answers a bridged MCP call, for the cloud host bridge —
3774
3786
  * the one tool path that does not reach {@link executeServerTool}, because
3775
3787
  * it forwards to `host.mcp` instead of invoking a tool body.
3776
3788
  *
3777
- * A bridged tool carries no `defineTool`, so it declares no effect, and the
3778
- * policy reads an undeclared tool as a write. In a dry run that answers
3779
- * bridged reads too; resolving effects from a server's own listing is the
3780
- * next unit's work.
3789
+ * A frozen session refuses forward-looking time args here even when it is
3790
+ * not a dry run. A bridged tool carries no `defineTool`, so it declares no
3791
+ * effect, and the policy reads an undeclared tool as a write. In a dry run
3792
+ * that answers bridged reads too; resolving effects from a server's own
3793
+ * listing is the next unit's work.
3781
3794
  */
3782
3795
  async bridgedCallAnswer(args: {
3783
3796
  sessionId: string;
3784
3797
  connectionName: string;
3785
3798
  toolName: string;
3799
+ input?: unknown;
3786
3800
  }): Promise<
3787
3801
  { answered: true; result: SDKCustomToolResult } | { answered: false }
3788
3802
  > {
3789
3803
  // Read off the in-flight turn's context rather than the session store:
3790
3804
  // the bridge already refuses a call with no active turn, so the record is
3791
- // in memory. That keeps this free for the sessions that are not dry runs,
3792
- // which is every session today — the listing below is only paid inside a
3793
- // dry run.
3805
+ // in memory. The listing below is only paid inside a dry run.
3794
3806
  const context = this.activeToolContexts.get(args.sessionId);
3807
+ if (context?.record.asOf !== undefined) {
3808
+ const decision = decideToolCall({
3809
+ dryRun: false,
3810
+ effect: undefined,
3811
+ input: args.input,
3812
+ asOf: context.record.asOf,
3813
+ });
3814
+ if (decision.kind === "unbounded") {
3815
+ this.logger(
3816
+ `[session] bridged tool ${args.toolName} refused, unbounded_time (${args.sessionId})`
3817
+ );
3818
+ return { answered: true, result: toolErrorResult(decision.message) };
3819
+ }
3820
+ }
3795
3821
  if (context?.record.dryRun !== true) {
3796
3822
  return { answered: false };
3797
3823
  }
@@ -3799,14 +3825,16 @@ export class SessionEngine {
3799
3825
  // server's own listing annotations; a tool the listing cannot classify
3800
3826
  // refuses rather than being stubbed. A failed listing classifies nothing,
3801
3827
  // which lands on the same refusal — never on a run.
3802
- const annotations = await this.bridgedToolAnnotations(
3803
- args.connectionName,
3804
- args.toolName
3805
- );
3828
+ const info = await this.bridgedToolInfo(args.connectionName, args.toolName);
3806
3829
  const decision = decideToolCall({
3807
3830
  dryRun: true,
3808
3831
  effect: undefined,
3809
- mcpTool: { name: args.toolName, annotations },
3832
+ mcpTool: { name: args.toolName, annotations: info?.annotations },
3833
+ input: args.input,
3834
+ asOf: context.record.asOf,
3835
+ ...(context.record.asOf !== undefined
3836
+ ? { timeWindow: schemaTimeWindowKeys(info?.inputSchema) }
3837
+ : {}),
3810
3838
  });
3811
3839
  if (decision.kind === "run") {
3812
3840
  return { answered: false };
@@ -3842,13 +3870,13 @@ export class SessionEngine {
3842
3870
  * undefined when the tool is unlisted, unannotated, or the listing fails —
3843
3871
  * all of which the policy reads as unclassified, the refusing state.
3844
3872
  */
3845
- private async bridgedToolAnnotations(
3873
+ private async bridgedToolInfo(
3846
3874
  connectionName: string,
3847
3875
  toolName: string
3848
- ): Promise<McpToolAnnotations | undefined> {
3876
+ ): Promise<HostMcpToolInfo | undefined> {
3849
3877
  try {
3850
3878
  const listed = await this.host.mcp.listTools(connectionName);
3851
- return listed.find((tool) => tool.name === toolName)?.annotations;
3879
+ return listed.find((tool) => tool.name === toolName);
3852
3880
  } catch {
3853
3881
  return undefined;
3854
3882
  }
@@ -14,7 +14,8 @@
14
14
  * session key with `processDeath: true` and the real throw. Soft-fail on
15
15
  * persist so a store outage never changes the hosted-execution HTTP reply.
16
16
  *
17
- * Keys: {@link storageKeys.sessionRun}.
17
+ * Same flush moments as `hosted-execution.json` and the start.log tee —
18
+ * see `hosted-execution-flush.ts`. Keys: {@link storageKeys.sessionRun}.
18
19
  */
19
20
 
20
21
  import type { FileSink } from "../files.js";
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Durable hosted `start.log` on the framework file store. **v2 only.**
3
+ *
4
+ * The engine tees stdout/stderr to a local file. That file is the v2
5
+ * customer log (errors, traces, debug). It is NOT written to S3 per line:
6
+ * callers snapshot the local buffer and PUT once. The Temporal host does
7
+ * a last catch-up at the end of the delivery so the object is complete
8
+ * after park/discard.
9
+ *
10
+ * Same flush moments as `hosted-execution.json` and session-runs —
11
+ * see `hosted-execution-flush.ts`. Keys: {@link storageKeys.log}.
12
+ * Values are raw UTF-8, not JSON.
13
+ */
14
+
15
+ import { readFile } from "node:fs/promises";
16
+ import type { FileSink } from "../files.js";
17
+ import {
18
+ cursorFrameworkStoreSourceId,
19
+ cursorHostedFiles,
20
+ } from "../files-backends/cursor-hosted.js";
21
+ import { storageKeys } from "../storage-protocol.js";
22
+ import type { AgentProject } from "../types.js";
23
+ import { resolveApiKeySync } from "./cursor/credentials.js";
24
+ import { AGENT_SERVE_GENERATION_ENV_VAR } from "./cursor-relay-core.js";
25
+ import { describeError } from "./describe-error.js";
26
+
27
+ /**
28
+ * Last N bytes flushed to the store. Matches the Temporal catch-up cap
29
+ * so guest snapshots and host catch-up stay interchangeable. The store
30
+ * allows 64 MiB; this keeps the dashboard snapshot bounded.
31
+ */
32
+ export const START_LOG_STORE_MAX_BYTES: number = 2 * 1024 * 1024;
33
+
34
+ /** Same path the hosted start command tees. Overridable in tests. */
35
+ export const DEFAULT_ENGINE_START_LOG_PATH =
36
+ "/workspace/.agent-serve/start.log";
37
+
38
+ export interface StartLogStore {
39
+ persist(startLogId: string, contents: string): Promise<void>;
40
+ read(startLogId: string): Promise<string | undefined>;
41
+ }
42
+
43
+ function decodeSinkBody(body: Uint8Array | { body: Uint8Array }): Uint8Array {
44
+ return body instanceof Uint8Array ? body : body.body;
45
+ }
46
+
47
+ export function tailStartLogText(raw: string, maxBytes: number): string {
48
+ const encoded = new TextEncoder().encode(raw);
49
+ if (encoded.byteLength <= maxBytes) {
50
+ return raw;
51
+ }
52
+ return new TextDecoder().decode(
53
+ encoded.subarray(encoded.byteLength - maxBytes)
54
+ );
55
+ }
56
+
57
+ export function resolveEngineStartLogPath(
58
+ env: NodeJS.ProcessEnv = process.env
59
+ ): string {
60
+ const configured = env.AGENT_SERVE_ENGINE_START_LOG_PATH?.trim();
61
+ return configured !== undefined && configured.length > 0
62
+ ? configured
63
+ : DEFAULT_ENGINE_START_LOG_PATH;
64
+ }
65
+
66
+ /**
67
+ * Read the local engine tee. Missing/empty is `undefined` (no store write).
68
+ * Soft-fail: a local read error must not change hosted-execution HTTP.
69
+ */
70
+ export async function readLocalStartLogText(args?: {
71
+ path?: string;
72
+ maxBytes?: number;
73
+ }): Promise<string | undefined> {
74
+ const path = args?.path ?? resolveEngineStartLogPath();
75
+ const maxBytes = args?.maxBytes ?? START_LOG_STORE_MAX_BYTES;
76
+ try {
77
+ const raw = await readFile(path, "utf8");
78
+ if (raw.length === 0) {
79
+ return undefined;
80
+ }
81
+ return tailStartLogText(raw, maxBytes);
82
+ } catch (error) {
83
+ if (
84
+ error !== null &&
85
+ typeof error === "object" &&
86
+ "code" in error &&
87
+ (error as { code?: string }).code === "ENOENT"
88
+ ) {
89
+ return undefined;
90
+ }
91
+ return undefined;
92
+ }
93
+ }
94
+
95
+ export function createStartLogStore(input: {
96
+ sink: FileSink;
97
+ agentName: string;
98
+ logger?: (line: string) => void;
99
+ }): StartLogStore {
100
+ const { sink, agentName } = input;
101
+ const logger = input.logger ?? (() => {});
102
+
103
+ return {
104
+ async persist(startLogId, contents) {
105
+ const key = storageKeys.log(agentName, startLogId);
106
+ const body = tailStartLogText(contents, START_LOG_STORE_MAX_BYTES);
107
+ try {
108
+ await sink.put(key, new TextEncoder().encode(body));
109
+ } catch (error) {
110
+ logger(
111
+ `[agent-sdk] start-log: persist failed for ${startLogId} (${describeError(error)})`
112
+ );
113
+ }
114
+ },
115
+
116
+ async read(startLogId) {
117
+ const key = storageKeys.log(agentName, startLogId);
118
+ try {
119
+ const body = await sink.get(key);
120
+ if (body === undefined) {
121
+ return undefined;
122
+ }
123
+ const text = new TextDecoder().decode(decodeSinkBody(body));
124
+ return text.length === 0 ? undefined : text;
125
+ } catch (error) {
126
+ logger(
127
+ `[agent-sdk] start-log: read failed for ${startLogId} (${describeError(error)})`
128
+ );
129
+ return undefined;
130
+ }
131
+ },
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Same selection as the session-run log: architecture-v2 hosted engines.
137
+ */
138
+ export function selectStartLogStore(input: {
139
+ project: AgentProject;
140
+ logger?: (line: string) => void;
141
+ env?: Record<string, string | undefined>;
142
+ }): StartLogStore | undefined {
143
+ if (input.project.agent.architecture !== "v2") {
144
+ return undefined;
145
+ }
146
+ const env = input.env ?? process.env;
147
+ if (env[AGENT_SERVE_GENERATION_ENV_VAR] === undefined) {
148
+ return undefined;
149
+ }
150
+ const envSnapshot = env as NodeJS.ProcessEnv;
151
+ return createStartLogStore({
152
+ sink: cursorHostedFiles({
153
+ resolveStoreSourceId: () => cursorFrameworkStoreSourceId(envSnapshot),
154
+ resolveApiKey: () => resolveApiKeySync()?.apiKey,
155
+ }),
156
+ agentName: input.project.name,
157
+ logger: input.logger,
158
+ });
159
+ }
160
+
161
+ /**
162
+ * Snapshot the local tee into the store. No-op when the file is missing
163
+ * or empty. Soft-fail inside {@link StartLogStore.persist}.
164
+ */
165
+ export async function flushLocalStartLog(args: {
166
+ store: StartLogStore;
167
+ startLogId: string;
168
+ path?: string;
169
+ }): Promise<void> {
170
+ const contents = await readLocalStartLogText(
171
+ args.path === undefined ? undefined : { path: args.path }
172
+ );
173
+ if (contents === undefined) {
174
+ return;
175
+ }
176
+ await args.store.persist(args.startLogId, contents);
177
+ }