@cursor/july 0.1.39 → 0.1.41

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 (259) hide show
  1. package/AGENTS.md +4 -3
  2. package/dist/bin/agent-serve.js +28 -9
  3. package/dist/channels/slack/api.d.ts.map +1 -1
  4. package/dist/channels/slack/api.js +18 -8
  5. package/dist/channels/slack/attachments.d.ts +82 -0
  6. package/dist/channels/slack/attachments.d.ts.map +1 -0
  7. package/dist/channels/slack/attachments.js +520 -0
  8. package/dist/channels/slack/cursor-account.d.ts +7 -1
  9. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  10. package/dist/channels/slack/cursor-account.js +2 -2
  11. package/dist/channels/slack/defaults.d.ts.map +1 -1
  12. package/dist/channels/slack/defaults.js +26 -17
  13. package/dist/channels/slack/dispatch.d.ts +4 -3
  14. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  15. package/dist/channels/slack/dispatch.js +84 -21
  16. package/dist/channels/slack/inbound.d.ts +6 -0
  17. package/dist/channels/slack/inbound.d.ts.map +1 -1
  18. package/dist/channels/slack/inbound.js +48 -2
  19. package/dist/channels/slack/index.d.ts +1 -0
  20. package/dist/channels/slack/index.d.ts.map +1 -1
  21. package/dist/channels/slack/index.js +1 -0
  22. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  23. package/dist/channels/slack/live-delivery.js +16 -4
  24. package/dist/channels/slack/manifest.d.ts.map +1 -1
  25. package/dist/channels/slack/manifest.js +1 -0
  26. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  27. package/dist/channels/slack/slack-channel.js +7 -4
  28. package/dist/channels/slack/types.d.ts +49 -1
  29. package/dist/channels/slack/types.d.ts.map +1 -1
  30. package/dist/docs/404.html +2 -2
  31. package/dist/docs/ab.html +6 -6
  32. package/dist/docs/assets/{ab.md.6cLOW7--.js → ab.md.hdxsvX5M.js} +2 -2
  33. package/dist/docs/assets/{app.BQ8Hihdf.js → app.mIw24pb1.js} +1 -1
  34. package/dist/docs/assets/{building-with-agents.md.txrcGU2B.js → building-with-agents.md.PeZaZA1P.js} +1 -1
  35. package/dist/docs/assets/chunks/@localSearchIndexroot.BA7V8dEJ.js +1 -0
  36. package/dist/docs/assets/chunks/{VPLocalSearchBox.BBCr8Yuy.js → VPLocalSearchBox.hOzpPwrG.js} +1 -1
  37. package/dist/docs/assets/chunks/{theme.CK_NiGC-.js → theme.BtFhUYmh.js} +2 -2
  38. package/dist/docs/assets/{deployment.md.CuK5SNjN.js → deployment.md.BHVjVqRa.js} +4 -4
  39. package/dist/docs/assets/{deployment.md.CuK5SNjN.lean.js → deployment.md.BHVjVqRa.lean.js} +1 -1
  40. package/dist/docs/assets/{evals.md.BQXI3rXy.js → evals.md.qeoGgPw0.js} +3 -3
  41. package/dist/docs/assets/example-agents_index.md.xmJ87d_3.js +2 -0
  42. package/dist/docs/assets/{guides_webhooks.md.BERuBSJW.js → guides_webhooks.md.DB-r_er9.js} +47 -4
  43. package/dist/docs/assets/{guides_webhooks.md.BERuBSJW.lean.js → guides_webhooks.md.DB-r_er9.lean.js} +1 -1
  44. package/dist/docs/assets/index.md.BKpbkCa6.js +24 -0
  45. package/dist/docs/assets/{index.md.CmhptOmN.lean.js → index.md.BKpbkCa6.lean.js} +1 -1
  46. package/dist/docs/assets/{quickstart.md.C_b6ESpD.js → quickstart.md.rIbfahJM.js} +4 -4
  47. package/dist/docs/assets/{reference_cli.md.Byvrg8eu.js → reference_cli.md.DqEqQpGn.js} +5 -5
  48. package/dist/docs/assets/{reference_cli.md.Byvrg8eu.lean.js → reference_cli.md.DqEqQpGn.lean.js} +1 -1
  49. package/dist/docs/assets/{reference_hooks.md.BGDw4VLm.js → reference_hooks.md.BHeG-vjK.js} +2 -2
  50. package/dist/docs/assets/{reference_hooks.md.BGDw4VLm.lean.js → reference_hooks.md.BHeG-vjK.lean.js} +1 -1
  51. package/dist/docs/assets/{storage.md.mQDtIULc.js → storage.md.DcGSnIxX.js} +10 -4
  52. package/dist/docs/assets/{storage.md.mQDtIULc.lean.js → storage.md.DcGSnIxX.lean.js} +1 -1
  53. package/dist/docs/assets/troubleshooting.md.CkgwmVI0.js +1 -0
  54. package/dist/docs/assets/{troubleshooting.md.B5RVX_tL.lean.js → troubleshooting.md.CkgwmVI0.lean.js} +1 -1
  55. package/dist/docs/building-with-agents.html +5 -5
  56. package/dist/docs/concepts.html +3 -3
  57. package/dist/docs/deployment.html +6 -6
  58. package/dist/docs/evals.html +7 -7
  59. package/dist/docs/example-agents/approval-buddy.html +3 -3
  60. package/dist/docs/example-agents/benny.html +3 -3
  61. package/dist/docs/example-agents/bugbot.html +3 -3
  62. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  63. package/dist/docs/example-agents/codeowners-review.html +3 -3
  64. package/dist/docs/example-agents/concierge.html +3 -3
  65. package/dist/docs/example-agents/fsd.html +3 -3
  66. package/dist/docs/example-agents/index.html +5 -5
  67. package/dist/docs/example-agents/knowledge-base.html +3 -3
  68. package/dist/docs/example-agents/oncall.html +3 -3
  69. package/dist/docs/example-agents/security-reviewer.html +3 -3
  70. package/dist/docs/example-agents/slack-agent.html +3 -3
  71. package/dist/docs/example-agents/weather-agent.html +3 -3
  72. package/dist/docs/guides/agent-to-agent.html +3 -3
  73. package/dist/docs/guides/cloud-runtime.html +3 -3
  74. package/dist/docs/guides/github.html +3 -3
  75. package/dist/docs/guides/human-in-the-loop.html +3 -3
  76. package/dist/docs/guides/mcp-oauth.html +3 -3
  77. package/dist/docs/guides/slack.html +3 -3
  78. package/dist/docs/guides/webhooks.html +49 -6
  79. package/dist/docs/hashmap.json +1 -1
  80. package/dist/docs/hillclimbing.html +3 -3
  81. package/dist/docs/index.html +7 -7
  82. package/dist/docs/quickstart.html +8 -8
  83. package/dist/docs/reference/agent-config.html +3 -3
  84. package/dist/docs/reference/artifacts.html +3 -3
  85. package/dist/docs/reference/channels.html +3 -3
  86. package/dist/docs/reference/cli.html +8 -8
  87. package/dist/docs/reference/connections.html +3 -3
  88. package/dist/docs/reference/hooks.html +5 -5
  89. package/dist/docs/reference/http-api.html +3 -3
  90. package/dist/docs/reference/instructions.html +3 -3
  91. package/dist/docs/reference/playground.html +3 -3
  92. package/dist/docs/reference/project-layout.html +3 -3
  93. package/dist/docs/reference/prompt.html +3 -3
  94. package/dist/docs/reference/schedules.html +3 -3
  95. package/dist/docs/reference/sessions.html +3 -3
  96. package/dist/docs/reference/skills.html +3 -3
  97. package/dist/docs/reference/subagents.html +3 -3
  98. package/dist/docs/reference/tools.html +3 -3
  99. package/dist/docs/scaffolding-agents.html +3 -3
  100. package/dist/docs/storage.html +14 -8
  101. package/dist/docs/troubleshooting.html +5 -5
  102. package/dist/index.d.ts +4 -2
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +3 -1
  105. package/dist/internal/cli-ax.d.ts.map +1 -1
  106. package/dist/internal/cli-ax.js +22 -2
  107. package/dist/internal/cli-deploy.d.ts +6 -0
  108. package/dist/internal/cli-deploy.d.ts.map +1 -1
  109. package/dist/internal/cli-deploy.js +14 -4
  110. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  111. package/dist/internal/cli-mcp-oauth.js +3 -1
  112. package/dist/internal/cli-mcp.js +4 -4
  113. package/dist/internal/cursor-event-relay.d.ts +45 -0
  114. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  115. package/dist/internal/cursor-event-relay.js +96 -2
  116. package/dist/internal/deploy-client.js +1 -1
  117. package/dist/internal/discovery.d.ts.map +1 -1
  118. package/dist/internal/discovery.js +35 -7
  119. package/dist/internal/distribution.d.ts.map +1 -1
  120. package/dist/internal/distribution.js +3 -1
  121. package/dist/internal/durable-session-store.d.ts +48 -0
  122. package/dist/internal/durable-session-store.d.ts.map +1 -0
  123. package/dist/internal/durable-session-store.js +86 -0
  124. package/dist/internal/hosting.d.ts +1 -1
  125. package/dist/internal/hosting.js +2 -2
  126. package/dist/internal/init-project.d.ts +34 -0
  127. package/dist/internal/init-project.d.ts.map +1 -1
  128. package/dist/internal/init-project.js +121 -2
  129. package/dist/internal/ledger-serving.d.ts +101 -0
  130. package/dist/internal/ledger-serving.d.ts.map +1 -0
  131. package/dist/internal/ledger-serving.js +174 -0
  132. package/dist/internal/listen-port.d.ts +22 -0
  133. package/dist/internal/listen-port.d.ts.map +1 -0
  134. package/dist/internal/listen-port.js +77 -0
  135. package/dist/internal/mcp-oauth.d.ts +12 -1
  136. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  137. package/dist/internal/mcp-oauth.js +25 -3
  138. package/dist/internal/playground/static.d.ts +21 -4
  139. package/dist/internal/playground/static.d.ts.map +1 -1
  140. package/dist/internal/playground/static.js +82 -18
  141. package/dist/internal/sdk-runner.d.ts.map +1 -1
  142. package/dist/internal/sdk-runner.js +10 -1
  143. package/dist/internal/server.d.ts.map +1 -1
  144. package/dist/internal/server.js +67 -49
  145. package/dist/internal/session-engine.d.ts +57 -1
  146. package/dist/internal/session-engine.d.ts.map +1 -1
  147. package/dist/internal/session-engine.js +117 -28
  148. package/dist/internal/slack-provision-client.js +1 -1
  149. package/dist/internal/workspace.d.ts +6 -2
  150. package/dist/internal/workspace.d.ts.map +1 -1
  151. package/dist/internal/workspace.js +10 -2
  152. package/dist/kv/memory-kv.d.ts +23 -0
  153. package/dist/kv/memory-kv.d.ts.map +1 -0
  154. package/dist/kv/memory-kv.js +103 -0
  155. package/dist/kv.d.ts +22 -0
  156. package/dist/kv.d.ts.map +1 -0
  157. package/dist/kv.js +20 -0
  158. package/dist/ledger/session-ledger.d.ts +204 -0
  159. package/dist/ledger/session-ledger.d.ts.map +1 -0
  160. package/dist/ledger/session-ledger.js +382 -0
  161. package/dist/ledger.d.ts +11 -0
  162. package/dist/ledger.d.ts.map +1 -0
  163. package/dist/ledger.js +9 -0
  164. package/dist/playground/assets/index-B3HBneZZ.js +85 -0
  165. package/dist/playground/assets/index-Bqk9nplh.css +1 -0
  166. package/dist/playground/index.html +2 -2
  167. package/dist/storage-backends/cursor-hosted.d.ts +25 -3
  168. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  169. package/dist/storage-backends/cursor-hosted.js +50 -2
  170. package/dist/storage-backends/file-kv.d.ts +5 -2
  171. package/dist/storage-backends/file-kv.d.ts.map +1 -1
  172. package/dist/storage-backends/file-kv.js +107 -7
  173. package/dist/storage.d.ts +60 -5
  174. package/dist/storage.d.ts.map +1 -1
  175. package/dist/storage.js +28 -4
  176. package/dist/types.d.ts +123 -3
  177. package/dist/types.d.ts.map +1 -1
  178. package/dist/types.js +14 -0
  179. package/docs/README.md +10 -7
  180. package/docs/ab.md +2 -2
  181. package/docs/building-with-agents.md +1 -1
  182. package/docs/deployment.md +34 -11
  183. package/docs/evals.md +3 -3
  184. package/docs/example-agents/index.md +1 -1
  185. package/docs/guides/webhooks.md +87 -0
  186. package/docs/quickstart.md +7 -8
  187. package/docs/reference/cli.md +15 -6
  188. package/docs/reference/hooks.md +8 -2
  189. package/docs/storage.md +67 -7
  190. package/docs/troubleshooting.md +10 -2
  191. package/package.json +20 -7
  192. package/skills/create-agent/SKILL.md +1 -1
  193. package/src/bin/agent-serve.ts +27 -11
  194. package/src/channels/slack/api.ts +19 -14
  195. package/src/channels/slack/attachments.ts +635 -0
  196. package/src/channels/slack/cursor-account.ts +8 -0
  197. package/src/channels/slack/defaults.ts +34 -18
  198. package/src/channels/slack/dispatch.ts +121 -6
  199. package/src/channels/slack/inbound.ts +60 -0
  200. package/src/channels/slack/index.ts +1 -0
  201. package/src/channels/slack/live-delivery.ts +17 -3
  202. package/src/channels/slack/manifest.ts +1 -0
  203. package/src/channels/slack/slack-channel.ts +7 -0
  204. package/src/channels/slack/types.ts +47 -1
  205. package/src/index.ts +9 -0
  206. package/src/internal/cli-ax.ts +25 -1
  207. package/src/internal/cli-deploy.ts +22 -4
  208. package/src/internal/cli-mcp-oauth.ts +3 -1
  209. package/src/internal/cli-mcp.ts +4 -4
  210. package/src/internal/cursor-event-relay.ts +168 -0
  211. package/src/internal/deploy-client.ts +1 -1
  212. package/src/internal/discovery.ts +42 -5
  213. package/src/internal/distribution.ts +3 -1
  214. package/src/internal/durable-session-store.ts +95 -0
  215. package/src/internal/hosting.ts +2 -2
  216. package/src/internal/init-project.ts +151 -2
  217. package/src/internal/ledger-serving.ts +240 -0
  218. package/src/internal/listen-port.ts +85 -0
  219. package/src/internal/mcp-oauth.ts +29 -4
  220. package/src/internal/playground/static.ts +111 -20
  221. package/src/internal/sdk-runner.ts +11 -1
  222. package/src/internal/server.ts +76 -62
  223. package/src/internal/session-engine.ts +192 -16
  224. package/src/internal/slack-provision-client.ts +1 -1
  225. package/src/internal/workspace.ts +14 -3
  226. package/src/kv/memory-kv.ts +99 -0
  227. package/src/kv.ts +27 -0
  228. package/src/ledger/session-ledger.ts +535 -0
  229. package/src/ledger.ts +29 -0
  230. package/src/storage-backends/cursor-hosted.ts +108 -3
  231. package/src/storage-backends/file-kv.ts +128 -12
  232. package/src/storage.ts +90 -6
  233. package/src/types.ts +130 -4
  234. package/dist/channels/slack/post-update-delivery.d.ts +0 -85
  235. package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
  236. package/dist/docs/assets/chunks/@localSearchIndexroot.B-VTH4As.js +0 -1
  237. package/dist/docs/assets/example-agents_index.md.DgGBwckv.js +0 -2
  238. package/dist/docs/assets/index.md.CmhptOmN.js +0 -24
  239. package/dist/docs/assets/troubleshooting.md.B5RVX_tL.js +0 -1
  240. package/dist/internal/json-dir-store.d.ts +0 -32
  241. package/dist/internal/json-dir-store.d.ts.map +0 -1
  242. package/dist/internal/persistence-coordinator.d.ts +0 -127
  243. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  244. package/dist/multi-tenant.d.ts +0 -80
  245. package/dist/multi-tenant.d.ts.map +0 -1
  246. package/dist/multi-tenant.js +0 -69
  247. package/dist/persistence.d.ts +0 -184
  248. package/dist/persistence.d.ts.map +0 -1
  249. package/dist/playground/assets/index-BRhBbnd4.css +0 -1
  250. package/dist/playground/assets/index-_2WKw9a-.js +0 -85
  251. package/dist/storage-backends/postgres-kv.d.ts +0 -82
  252. package/dist/storage-backends/postgres-kv.d.ts.map +0 -1
  253. package/dist/storage-backends/postgres-kv.js +0 -185
  254. package/src/storage-backends/postgres-kv.ts +0 -253
  255. /package/dist/docs/assets/{ab.md.6cLOW7--.lean.js → ab.md.hdxsvX5M.lean.js} +0 -0
  256. /package/dist/docs/assets/{building-with-agents.md.txrcGU2B.lean.js → building-with-agents.md.PeZaZA1P.lean.js} +0 -0
  257. /package/dist/docs/assets/{evals.md.BQXI3rXy.lean.js → evals.md.qeoGgPw0.lean.js} +0 -0
  258. /package/dist/docs/assets/{example-agents_index.md.DgGBwckv.lean.js → example-agents_index.md.xmJ87d_3.lean.js} +0 -0
  259. /package/dist/docs/assets/{quickstart.md.C_b6ESpD.lean.js → quickstart.md.rIbfahJM.lean.js} +0 -0
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  });
12
12
  };
13
13
  import { spawn } from "node:child_process";
14
+ import { existsSync } from "node:fs";
14
15
  import { mkdir, writeFile } from "node:fs/promises";
15
16
  import { basename, dirname, join, relative, resolve } from "node:path";
16
17
  import { CLI_COMMAND_NAME, PACKAGE_NAME, packageVersion, } from "./distribution.js";
@@ -70,6 +71,7 @@ export function initProject(options) {
70
71
  description: `agent project: ${name}`,
71
72
  scripts: {
72
73
  check: "tsc --noEmit",
74
+ dev: "agent-sdk dev",
73
75
  },
74
76
  dependencies: {
75
77
  [PACKAGE_NAME]: `^${packageVersion()}`,
@@ -176,6 +178,117 @@ export function installScaffoldDependencies(rootDir_1) {
176
178
  };
177
179
  });
178
180
  }
181
+ /**
182
+ * Put `agent-sdk` on the user PATH by installing the already-downloaded
183
+ * project copy into the npm global prefix. `npx @cursor/july init` only
184
+ * runs the package; this is the install that makes bare `agent-sdk …`
185
+ * work afterward.
186
+ */
187
+ export function linkCliOntoPath(rootDir_1) {
188
+ return __awaiter(this, arguments, void 0, function* (rootDir, options = {}) {
189
+ var _a, _b, _c;
190
+ const packageDir = join(rootDir, "node_modules", PACKAGE_NAME);
191
+ if (!existsSync(packageDir)) {
192
+ return {
193
+ ok: false,
194
+ exitCode: 1,
195
+ stderr: `missing ${PACKAGE_NAME} under ${packageDir} (run npm install first)`,
196
+ };
197
+ }
198
+ const stdio = (_a = options.stdio) !== null && _a !== void 0 ? _a : "inherit";
199
+ const command = process.platform === "win32" ? "npm.cmd" : "npm";
200
+ const args = ["install", "--global", "--no-fund", "--no-audit", packageDir];
201
+ const runner = (_b = options.runner) !== null && _b !== void 0 ? _b : defaultNpmInstallRunner;
202
+ const result = yield runner({ command, args, cwd: rootDir, stdio });
203
+ if (result.exitCode !== 0) {
204
+ return {
205
+ ok: false,
206
+ exitCode: result.exitCode,
207
+ stderr: result.stderr,
208
+ };
209
+ }
210
+ const whichImpl = (_c = options.whichImpl) !== null && _c !== void 0 ? _c : resolveLinkedCli;
211
+ const resolvedPath = yield whichImpl(CLI_COMMAND_NAME);
212
+ return {
213
+ ok: resolvedPath !== undefined,
214
+ exitCode: 0,
215
+ stderr: resolvedPath === undefined
216
+ ? `${CLI_COMMAND_NAME} linked into the npm global prefix but is not on PATH; open a new shell or add that bin directory to PATH`
217
+ : "",
218
+ resolvedPath,
219
+ };
220
+ });
221
+ }
222
+ /** Prefer the npm-global bin; ignore an older `agent-sdk` earlier on PATH. */
223
+ function resolveLinkedCli(commandName) {
224
+ return __awaiter(this, void 0, void 0, function* () {
225
+ var _a;
226
+ const prefix = yield npmGlobalPrefix();
227
+ if (prefix === undefined) {
228
+ return whichOnPath(commandName);
229
+ }
230
+ const binDir = process.platform === "win32" ? prefix : join(prefix, "bin");
231
+ const exts = process.platform === "win32"
232
+ ? ((_a = process.env.PATHEXT) !== null && _a !== void 0 ? _a : ".EXE;.CMD;.BAT").split(";").filter(Boolean)
233
+ : [""];
234
+ for (const ext of exts) {
235
+ const candidate = join(binDir, `${commandName}${ext}`);
236
+ if (!existsSync(candidate)) {
237
+ continue;
238
+ }
239
+ // Only claim PATH success when this linked bin is the first hit.
240
+ const onPath = whichOnPath(commandName);
241
+ return onPath === candidate ? candidate : undefined;
242
+ }
243
+ return undefined;
244
+ });
245
+ }
246
+ function npmGlobalPrefix() {
247
+ return __awaiter(this, void 0, void 0, function* () {
248
+ const command = process.platform === "win32" ? "npm.cmd" : "npm";
249
+ return yield new Promise((resolvePromise) => {
250
+ var _a, _b;
251
+ const child = spawn(command, ["prefix", "-g"], {
252
+ stdio: ["ignore", "pipe", "ignore"],
253
+ env: process.env,
254
+ });
255
+ let out = "";
256
+ (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.setEncoding("utf8");
257
+ (_b = child.stdout) === null || _b === void 0 ? void 0 : _b.on("data", (chunk) => {
258
+ out += chunk;
259
+ });
260
+ child.on("error", () => resolvePromise(undefined));
261
+ child.on("close", (code) => {
262
+ if (code !== 0) {
263
+ resolvePromise(undefined);
264
+ return;
265
+ }
266
+ const prefix = out.trim();
267
+ resolvePromise(prefix === "" ? undefined : prefix);
268
+ });
269
+ });
270
+ });
271
+ }
272
+ function whichOnPath(commandName) {
273
+ var _a, _b;
274
+ const pathEnv = (_a = process.env.PATH) !== null && _a !== void 0 ? _a : "";
275
+ const sep = process.platform === "win32" ? ";" : ":";
276
+ const exts = process.platform === "win32"
277
+ ? ((_b = process.env.PATHEXT) !== null && _b !== void 0 ? _b : ".EXE;.CMD;.BAT").split(";").filter(Boolean)
278
+ : [""];
279
+ for (const dir of pathEnv.split(sep)) {
280
+ if (dir === "") {
281
+ continue;
282
+ }
283
+ for (const ext of exts) {
284
+ const candidate = join(dir, `${commandName}${ext}`);
285
+ if (existsSync(candidate)) {
286
+ return candidate;
287
+ }
288
+ }
289
+ }
290
+ return undefined;
291
+ }
179
292
  function defaultNpmInstallRunner(args) {
180
293
  return __awaiter(this, void 0, void 0, function* () {
181
294
  return yield new Promise((resolvePromise, reject) => {
@@ -286,6 +399,12 @@ function initNextStepEntries(options) {
286
399
  if (cd !== undefined) {
287
400
  steps.push({ command: cd });
288
401
  }
402
+ if (!options.cliOnPath) {
403
+ steps.push({
404
+ command: 'export PATH="$PWD/node_modules/.bin:$PATH"',
405
+ note: "use the project-local agent-sdk bin",
406
+ });
407
+ }
289
408
  if (!options.signedIn) {
290
409
  steps.push({
291
410
  command: `${CLI_COMMAND_NAME} login`,
@@ -313,10 +432,10 @@ export function printInitGuide() {
313
432
  process.stdout.write(`${CLI_COMMAND_NAME} — coding agent setup
314
433
 
315
434
  Scaffold a project, then open the playground:
316
- ${CLI_COMMAND_NAME} init ./my-agent # installs deps; signs you in when needed
435
+ npx ${PACKAGE_NAME} init ./my-agent # installs deps + puts agent-sdk on PATH
317
436
  cd ./my-agent && ${CLI_COMMAND_NAME} dev
318
437
 
319
- Recommended loop once the project exists:
438
+ Recommended loop once the project exists (from the project directory):
320
439
  1. Edit agent/ + tools under agent/tools/
321
440
  2. ${CLI_COMMAND_NAME} info --dir . --json
322
441
  3. ${CLI_COMMAND_NAME} run --dir . --message "..."
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Ledger-mode serving seam (`serving: "ledger"` on `defineAgent`):
3
+ * coordination only, no inbox. Every engine `send` acquires the session
4
+ * lease before its turn runs and commits (head+1, lease released) after
5
+ * it completes, so cross-pod mutual exclusion, generation fencing, and
6
+ * the committed head sequence are real.
7
+ *
8
+ * **Durability trade-off (deliberate):** there is no durable mailbox.
9
+ * An event that arrives while the lease is held does NOT park in the
10
+ * store — the delivering pod waits *in-process*: local concurrent sends
11
+ * serialize on a per-session promise chain (no claim spinning), and a
12
+ * lease held by another pod is waited out with bounded claim retries. A
13
+ * crash therefore loses the in-flight event exactly as resident serving
14
+ * does. A dead pod's lease simply expires (~30s TTL); the next delivery
15
+ * claims at generation+1 — no janitor is needed because nothing is
16
+ * parked.
17
+ *
18
+ * Re-entrancy caveat (same shape as resident `waitForCompletion`): a
19
+ * send issued from *inside* a running turn against the same session
20
+ * chains behind that turn's serve, so awaiting its resolution from the
21
+ * turn would deadlock. Fire-and-forget such sends.
22
+ */
23
+ import type { SessionLedger } from "../ledger/session-ledger.js";
24
+ import type { ChannelSession, SendMessageOptions } from "../types.js";
25
+ /** Wait between claim retries while another holder has the lease. */
26
+ export declare const CLAIM_RETRY_DELAY_MS = 250;
27
+ /**
28
+ * Give up waiting for the lease after this long — two full lease TTLs,
29
+ * so a dead holder's expiry (~30s) is always survived with margin.
30
+ */
31
+ export declare const CLAIM_WAIT_TIMEOUT_MS: number;
32
+ /** The `send` options a ledger delivery carries to the resident turn. */
33
+ export type LedgerDeliverOptions = SendMessageOptions & {
34
+ mode?: "chat" | "task";
35
+ };
36
+ /** One delivery handed back to the engine's resident path. */
37
+ export interface LedgerTurnRequest {
38
+ channelId: string;
39
+ continuationToken: string;
40
+ message: string;
41
+ options: LedgerDeliverOptions;
42
+ }
43
+ export interface LedgerServingOptions {
44
+ ledger: SessionLedger;
45
+ /**
46
+ * Run one delivery as a resident-path turn (the engine's `send` with
47
+ * the internal ledger-delivery flag). Must resolve once the turn is
48
+ * admitted; the pump awaits `waitForCompletion` itself.
49
+ */
50
+ deliverTurn: (request: LedgerTurnRequest) => Promise<ChannelSession>;
51
+ logger?: (line: string) => void;
52
+ /** Lease heartbeat cadence while the turn runs (default 10s). */
53
+ heartbeatIntervalMs?: number;
54
+ /** Claim retry cadence while the lease is held (default 250ms). */
55
+ claimRetryDelayMs?: number;
56
+ /** Give-up bound for lease waiting (default 2× lease TTL). */
57
+ claimWaitTimeoutMs?: number;
58
+ clock?: () => number;
59
+ /** Injectable wait (tests use short real delays; keep them tiny). */
60
+ sleep?: (ms: number) => Promise<void>;
61
+ }
62
+ export declare class LedgerServing {
63
+ private readonly ledger;
64
+ private readonly deliverTurn;
65
+ private readonly logger;
66
+ private readonly heartbeatIntervalMs;
67
+ private readonly claimRetryDelayMs;
68
+ private readonly claimWaitTimeoutMs;
69
+ private readonly clock;
70
+ private readonly sleep;
71
+ /**
72
+ * Per-ledger-session in-process serialization: local concurrent sends
73
+ * queue here instead of spinning claim retries against our own lease.
74
+ */
75
+ private readonly chains;
76
+ constructor(options: LedgerServingOptions);
77
+ /** Settle every queued and in-flight delivery. */
78
+ close(): Promise<void>;
79
+ /**
80
+ * Ledger-mode `send`: resolve the session identity, wait for the
81
+ * lease (in-process queue first, then cross-pod claim retries), run
82
+ * the turn, commit. Resolves with the engine session handle after the
83
+ * commit released the lease.
84
+ */
85
+ deliver(channelId: string, message: string, options: LedgerDeliverOptions): Promise<ChannelSession>;
86
+ /** Chain a delivery behind the session's in-flight ones. */
87
+ private enqueue;
88
+ /** claim (waiting) → resident turn → commit-release. */
89
+ private serveOne;
90
+ /**
91
+ * Claim the lease, waiting out another holder with bounded retries.
92
+ * The common waits are short: our own pod's deliveries never reach
93
+ * here concurrently (the in-process chain serializes them), so a held
94
+ * lease means another pod is serving — usually for one turn — or died
95
+ * (expiry unblocks us at ~30s).
96
+ */
97
+ private claimWithWait;
98
+ }
99
+ /** Mint the lease holder id for this process. */
100
+ export declare function newLedgerPodId(): string;
101
+ //# sourceMappingURL=ledger-serving.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-serving.d.ts","sourceRoot":"","sources":["../../src/internal/ledger-serving.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAMjE,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGtE,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAyB,CAAC;AAE9D,yEAAyE;AACzE,MAAM,MAAM,oBAAoB,GAAG,kBAAkB,GAAG;IACtD,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB,CAAC;AAEF,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB;;;;OAIG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACrE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mEAAmE;IACnE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IACrB,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAE9D,YAAY,OAAO,EAAE,oBAAoB,EAaxC;IAED,kDAAkD;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAED;;;;;OAKG;IACG,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAazB;IAED,4DAA4D;IAC5D,OAAO,CAAC,OAAO;IAoBf,wDAAwD;YAC1C,QAAQ;IAsDtB;;;;;;OAMG;YACW,aAAa;CAiB5B;AAED,iDAAiD;AACjD,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Ledger-mode serving seam (`serving: "ledger"` on `defineAgent`):
3
+ * coordination only, no inbox. Every engine `send` acquires the session
4
+ * lease before its turn runs and commits (head+1, lease released) after
5
+ * it completes, so cross-pod mutual exclusion, generation fencing, and
6
+ * the committed head sequence are real.
7
+ *
8
+ * **Durability trade-off (deliberate):** there is no durable mailbox.
9
+ * An event that arrives while the lease is held does NOT park in the
10
+ * store — the delivering pod waits *in-process*: local concurrent sends
11
+ * serialize on a per-session promise chain (no claim spinning), and a
12
+ * lease held by another pod is waited out with bounded claim retries. A
13
+ * crash therefore loses the in-flight event exactly as resident serving
14
+ * does. A dead pod's lease simply expires (~30s TTL); the next delivery
15
+ * claims at generation+1 — no janitor is needed because nothing is
16
+ * parked.
17
+ *
18
+ * Re-entrancy caveat (same shape as resident `waitForCompletion`): a
19
+ * send issued from *inside* a running turn against the same session
20
+ * chains behind that turn's serve, so awaiting its resolution from the
21
+ * turn would deadlock. Fire-and-forget such sends.
22
+ */
23
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
24
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
25
+ return new (P || (P = Promise))(function (resolve, reject) {
26
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
27
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
28
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
29
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
30
+ });
31
+ };
32
+ import { randomBytes, randomUUID } from "node:crypto";
33
+ import { HEARTBEAT_INTERVAL_MS, LEASE_TTL_MS, LedgerFencedError, } from "../ledger/session-ledger.js";
34
+ import { newContinuationKey } from "./ids.js";
35
+ /** Wait between claim retries while another holder has the lease. */
36
+ export const CLAIM_RETRY_DELAY_MS = 250;
37
+ /**
38
+ * Give up waiting for the lease after this long — two full lease TTLs,
39
+ * so a dead holder's expiry (~30s) is always survived with margin.
40
+ */
41
+ export const CLAIM_WAIT_TIMEOUT_MS = 2 * LEASE_TTL_MS;
42
+ export class LedgerServing {
43
+ constructor(options) {
44
+ var _a, _b, _c, _d, _e, _f;
45
+ /**
46
+ * Per-ledger-session in-process serialization: local concurrent sends
47
+ * queue here instead of spinning claim retries against our own lease.
48
+ */
49
+ this.chains = new Map();
50
+ this.ledger = options.ledger;
51
+ this.deliverTurn = options.deliverTurn;
52
+ this.logger = (_a = options.logger) !== null && _a !== void 0 ? _a : (() => { });
53
+ this.heartbeatIntervalMs = (_b = options.heartbeatIntervalMs) !== null && _b !== void 0 ? _b : HEARTBEAT_INTERVAL_MS;
54
+ this.claimRetryDelayMs = (_c = options.claimRetryDelayMs) !== null && _c !== void 0 ? _c : CLAIM_RETRY_DELAY_MS;
55
+ this.claimWaitTimeoutMs = (_d = options.claimWaitTimeoutMs) !== null && _d !== void 0 ? _d : CLAIM_WAIT_TIMEOUT_MS;
56
+ this.clock = (_e = options.clock) !== null && _e !== void 0 ? _e : Date.now;
57
+ this.sleep = (_f = options.sleep) !== null && _f !== void 0 ? _f : ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
58
+ }
59
+ /** Settle every queued and in-flight delivery. */
60
+ close() {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ yield Promise.allSettled([...this.chains.values()]);
63
+ });
64
+ }
65
+ /**
66
+ * Ledger-mode `send`: resolve the session identity, wait for the
67
+ * lease (in-process queue first, then cross-pod claim retries), run
68
+ * the turn, commit. Resolves with the engine session handle after the
69
+ * commit released the lease.
70
+ */
71
+ deliver(channelId, message, options) {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ var _a;
74
+ const continuationToken = (_a = options.continuationToken) !== null && _a !== void 0 ? _a : newContinuationKey();
75
+ const ensured = yield this.ledger.ensureSession(`${channelId}/${continuationToken}`);
76
+ return this.enqueue(ensured.session.sessionId, () => this.serveOne(ensured.session.sessionId, {
77
+ channelId,
78
+ continuationToken,
79
+ message,
80
+ options,
81
+ }));
82
+ });
83
+ }
84
+ /** Chain a delivery behind the session's in-flight ones. */
85
+ enqueue(ledgerSessionId, run) {
86
+ var _a;
87
+ const prior = (_a = this.chains.get(ledgerSessionId)) !== null && _a !== void 0 ? _a : Promise.resolve();
88
+ const next = prior.then(() => run(), () => run());
89
+ this.chains.set(ledgerSessionId, next);
90
+ void next
91
+ .catch(() => { })
92
+ .finally(() => {
93
+ if (this.chains.get(ledgerSessionId) === next) {
94
+ this.chains.delete(ledgerSessionId);
95
+ }
96
+ });
97
+ return next;
98
+ }
99
+ /** claim (waiting) → resident turn → commit-release. */
100
+ serveOne(ledgerSessionId, request) {
101
+ return __awaiter(this, void 0, void 0, function* () {
102
+ var _a;
103
+ const lease = yield this.claimWithWait(ledgerSessionId);
104
+ let fenced = false;
105
+ const heartbeat = setInterval(() => {
106
+ this.ledger.heartbeat(ledgerSessionId, lease.generation).then((result) => {
107
+ if (!result.ok) {
108
+ fenced = true;
109
+ }
110
+ }, () => {
111
+ // Transient KV failure: the next beat retries within the TTL.
112
+ });
113
+ }, this.heartbeatIntervalMs);
114
+ (_a = heartbeat.unref) === null || _a === void 0 ? void 0 : _a.call(heartbeat);
115
+ try {
116
+ let handle;
117
+ try {
118
+ handle = yield this.deliverTurn(request);
119
+ yield handle.waitForCompletion();
120
+ }
121
+ catch (error) {
122
+ // The turn never committed; free the session for the next
123
+ // delivery instead of making it wait out the TTL. A fenced
124
+ // holder's release is a no-op.
125
+ yield this.ledger
126
+ .release(ledgerSessionId, lease.generation)
127
+ .catch(() => { });
128
+ throw error;
129
+ }
130
+ if (fenced) {
131
+ // A new holder took over mid-turn (our lease lapsed). Discard:
132
+ // committing would fence anyway, and warm state is now stale.
133
+ this.logger(`[ledger] session ${ledgerSessionId} fenced mid-turn (generation ${lease.generation}); discarding without commit`);
134
+ throw new LedgerFencedError(`deliver(${ledgerSessionId}): fenced mid-turn at generation ${lease.generation}`);
135
+ }
136
+ yield this.ledger.commit(ledgerSessionId, {
137
+ generation: lease.generation,
138
+ expectedHead: lease.head,
139
+ commitId: randomUUID(),
140
+ });
141
+ return handle;
142
+ }
143
+ finally {
144
+ clearInterval(heartbeat);
145
+ }
146
+ });
147
+ }
148
+ /**
149
+ * Claim the lease, waiting out another holder with bounded retries.
150
+ * The common waits are short: our own pod's deliveries never reach
151
+ * here concurrently (the in-process chain serializes them), so a held
152
+ * lease means another pod is serving — usually for one turn — or died
153
+ * (expiry unblocks us at ~30s).
154
+ */
155
+ claimWithWait(ledgerSessionId) {
156
+ return __awaiter(this, void 0, void 0, function* () {
157
+ const start = this.clock();
158
+ for (;;) {
159
+ const claim = yield this.ledger.claim(ledgerSessionId);
160
+ if (claim.claimed) {
161
+ return { generation: claim.lease.generation, head: claim.head };
162
+ }
163
+ if (this.clock() - start >= this.claimWaitTimeoutMs) {
164
+ throw new Error(`ledger serving: session ${ledgerSessionId} is still leased by ${claim.holder} after ${this.claimWaitTimeoutMs}ms; giving up on this delivery`);
165
+ }
166
+ yield this.sleep(this.claimRetryDelayMs);
167
+ }
168
+ });
169
+ }
170
+ }
171
+ /** Mint the lease holder id for this process. */
172
+ export function newLedgerPodId() {
173
+ return `pod_${randomBytes(6).toString("hex")}`;
174
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Bind an HTTP server to a port, with a clear next-port path when 3000 (or
3
+ * whatever was requested) is already taken — a common onboarding failure.
4
+ */
5
+ import type { Server } from "node:http";
6
+ /** Default listen port for `serve` / `dev` when `--port` is omitted. */
7
+ export declare const DEFAULT_SERVE_PORT = 3000;
8
+ export declare function isAddrInUse(error: unknown): boolean;
9
+ export declare function formatPortInUseHint(port: number): string;
10
+ /**
11
+ * `server.listen` with fallback when the caller used the default port.
12
+ * Explicit `--port N` (including `0`) fails immediately with a next-port hint.
13
+ */
14
+ export declare function listenHttpServer(args: {
15
+ server: Server;
16
+ host: string;
17
+ port: number;
18
+ /** True when the caller passed `port` (including 0); skip fallback. */
19
+ portWasExplicit: boolean;
20
+ logger: (line: string) => void;
21
+ }): Promise<number>;
22
+ //# sourceMappingURL=listen-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen-port.d.ts","sourceRoot":"","sources":["../../src/internal/listen-port.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGxC,wEAAwE;AACxE,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAKvC,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOnD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMxD;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC,GAAG,OAAO,CAAC,MAAM,CAAC,CAyClB"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Bind an HTTP server to a port, with a clear next-port path when 3000 (or
3
+ * whatever was requested) is already taken — a common onboarding failure.
4
+ */
5
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
+ return new (P || (P = Promise))(function (resolve, reject) {
8
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
12
+ });
13
+ };
14
+ import { once } from "node:events";
15
+ import { CLI_COMMAND_NAME } from "./distribution.js";
16
+ /** Default listen port for `serve` / `dev` when `--port` is omitted. */
17
+ export const DEFAULT_SERVE_PORT = 3000;
18
+ /** How many ports above the default to try when the default is taken. */
19
+ const DEFAULT_PORT_FALLBACK_SPAN = 20;
20
+ export function isAddrInUse(error) {
21
+ return (typeof error === "object" &&
22
+ error !== null &&
23
+ "code" in error &&
24
+ error.code === "EADDRINUSE");
25
+ }
26
+ export function formatPortInUseHint(port) {
27
+ const next = port === 0 ? 3001 : port + 1;
28
+ return (`port ${port} is already in use. ` +
29
+ `Try \`${CLI_COMMAND_NAME} dev --port ${next}\` or \`--port 0\` for any free port.`);
30
+ }
31
+ /**
32
+ * `server.listen` with fallback when the caller used the default port.
33
+ * Explicit `--port N` (including `0`) fails immediately with a next-port hint.
34
+ */
35
+ export function listenHttpServer(args) {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ const { server, host, logger } = args;
38
+ const startPort = args.port;
39
+ if (args.portWasExplicit || startPort === 0) {
40
+ try {
41
+ server.listen(startPort, host);
42
+ yield once(server, "listening");
43
+ }
44
+ catch (error) {
45
+ if (isAddrInUse(error)) {
46
+ throw new Error(formatPortInUseHint(startPort));
47
+ }
48
+ throw error;
49
+ }
50
+ const address = server.address();
51
+ if (address === null || typeof address === "string") {
52
+ throw new Error("server listening without an address");
53
+ }
54
+ return address.port;
55
+ }
56
+ const last = startPort + DEFAULT_PORT_FALLBACK_SPAN;
57
+ for (let port = startPort; port <= last; port++) {
58
+ try {
59
+ server.listen(port, host);
60
+ yield once(server, "listening");
61
+ if (port !== startPort) {
62
+ logger(`[agent-sdk] port ${startPort} in use; listening on ${port} instead (pass --port to pick)`);
63
+ }
64
+ return port;
65
+ }
66
+ catch (error) {
67
+ if (!isAddrInUse(error)) {
68
+ throw error;
69
+ }
70
+ if (port === last) {
71
+ throw new Error(formatPortInUseHint(port));
72
+ }
73
+ }
74
+ }
75
+ throw new Error(formatPortInUseHint(last));
76
+ });
77
+ }
@@ -78,7 +78,18 @@ export type OAuthCallbackResult = {
78
78
  code: string;
79
79
  state?: string;
80
80
  };
81
- export declare function waitForMcpOAuthCallback(args?: {
81
+ /**
82
+ * Deliberately not `@anysphere/mcp-core/oauth/loopback`, which is the canonical
83
+ * implementation every other Cursor surface shares. `@cursor/july` publishes to
84
+ * public npm with no private workspace dependencies, and its OAuth state is a
85
+ * bare UUID rather than the payload that module's validator expects.
86
+ *
87
+ * Nothing here reads Statsig, so this listener is outside
88
+ * `unified_mcp_loopback`: turning that gate off restores the other surfaces'
89
+ * original servers but leaves this one, and its state check, in place.
90
+ */
91
+ export declare function waitForMcpOAuthCallback(args: {
92
+ authorizationUrl: string;
82
93
  port?: number;
83
94
  path?: string;
84
95
  timeoutMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-oauth.d.ts","sourceRoot":"","sources":["../../src/internal/mcp-oauth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mcp-oauth.d.ts","sourceRoot":"","sources":["../../src/internal/mcp-oauth.ts"],"names":[],"mappings":"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"}
@@ -307,12 +307,23 @@ export function createHostMcpOAuthProvider(args) {
307
307
  });
308
308
  });
309
309
  }
310
+ /**
311
+ * Deliberately not `@anysphere/mcp-core/oauth/loopback`, which is the canonical
312
+ * implementation every other Cursor surface shares. `@cursor/july` publishes to
313
+ * public npm with no private workspace dependencies, and its OAuth state is a
314
+ * bare UUID rather than the payload that module's validator expects.
315
+ *
316
+ * Nothing here reads Statsig, so this listener is outside
317
+ * `unified_mcp_loopback`: turning that gate off restores the other surfaces'
318
+ * original servers but leaves this one, and its state check, in place.
319
+ */
310
320
  export function waitForMcpOAuthCallback(args) {
311
321
  return __awaiter(this, void 0, void 0, function* () {
312
322
  var _a, _b, _c;
313
- const port = (_a = args === null || args === void 0 ? void 0 : args.port) !== null && _a !== void 0 ? _a : MCP_OAUTH_CALLBACK_PORT;
314
- const path = (_b = args === null || args === void 0 ? void 0 : args.path) !== null && _b !== void 0 ? _b : MCP_OAUTH_CALLBACK_PATH;
315
- const timeoutMs = (_c = args === null || args === void 0 ? void 0 : args.timeoutMs) !== null && _c !== void 0 ? _c : 5 * 60 * 1000;
323
+ const port = (_a = args.port) !== null && _a !== void 0 ? _a : MCP_OAUTH_CALLBACK_PORT;
324
+ const path = (_b = args.path) !== null && _b !== void 0 ? _b : MCP_OAUTH_CALLBACK_PATH;
325
+ const timeoutMs = (_c = args.timeoutMs) !== null && _c !== void 0 ? _c : 5 * 60 * 1000;
326
+ const expectedState = new URL(args.authorizationUrl).searchParams.get("state");
316
327
  return new Promise((resolve, reject) => {
317
328
  let done = false;
318
329
  const finish = (result, error) => {
@@ -344,6 +355,17 @@ export function waitForMcpOAuthCallback(args) {
344
355
  const code = url.searchParams.get("code");
345
356
  const oauthError = url.searchParams.get("error");
346
357
  const state = (_b = url.searchParams.get("state")) !== null && _b !== void 0 ? _b : undefined;
358
+ // Without this, any page that can reach the loopback port during a login
359
+ // can inject its own authorization code into the exchange. Ignore what is
360
+ // not ours rather than failing the login: treating a stray request as
361
+ // fatal would let any page cancel a login, and free the port for someone
362
+ // else to bind before the real callback arrives.
363
+ if (state !== (expectedState !== null && expectedState !== void 0 ? expectedState : undefined)) {
364
+ res
365
+ .writeHead(404, { "content-type": "text/html; charset=utf-8" })
366
+ .end("<!doctype html><html><body><h1>Not found</h1></body></html>");
367
+ return;
368
+ }
347
369
  if (code) {
348
370
  res
349
371
  .writeHead(200, { "content-type": "text/html; charset=utf-8" })