@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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Serves the built Vite playground SPA and the multi-agent index page.
2
+ * Serves the built Vite playground SPA and the host home page.
3
3
  *
4
4
  * The playground is a Vite + React app (see `playground/`). `serve` auto-builds
5
5
  * `dist/playground/` when the local vite toolchain is present (see
@@ -93,33 +93,111 @@ export function renderPlaygroundAsset(file: string): Response {
93
93
  });
94
94
  }
95
95
 
96
- /** One row on the multi-agent index page. */
96
+ /** Slack surface summary for the host home / `GET /v1/agents`. */
97
+ export type AgentIndexSlack =
98
+ | { kind: "cursor-account"; handle: string }
99
+ | { kind: "socket" };
100
+
101
+ /** One row on the host home page and `GET /v1/agents`. */
97
102
  export interface AgentIndexEntry {
98
103
  slug: string;
99
104
  name: string;
100
105
  model: string;
101
106
  tools: number;
102
107
  channels: number;
108
+ slack?: AgentIndexSlack;
109
+ }
110
+
111
+ export interface HostHomeOptions {
112
+ /** When false, omit `/docs` from the route list. Default true. */
113
+ docs?: boolean;
114
+ /** Same as the serve host's `multi` — mounts `/v1/agents` + host `/v1/health`. */
115
+ multi?: boolean;
103
116
  }
104
117
 
105
- /** Standalone index page listing every mounted agent (multi-agent serving). */
106
- export function renderAgentsIndexHtml(agents: AgentIndexEntry[]): string {
118
+ /**
119
+ * Orienting `/` page: mounted agents (playground links) plus live host routes.
120
+ * Used for both single-agent (empty slug) and multi-agent mounts.
121
+ */
122
+ export function renderAgentsIndexHtml(
123
+ agents: AgentIndexEntry[],
124
+ options: HostHomeOptions = {}
125
+ ): string {
126
+ const docs = options.docs !== false;
127
+ const multi = options.multi === true;
107
128
  const cards = agents
108
129
  .map((agent) => {
130
+ const playground =
131
+ agent.slug === "" ? "/playground" : `/${agent.slug}/playground`;
132
+ const slugLabel = agent.slug === "" ? "/" : `/${agent.slug}`;
109
133
  const meta = `${agent.model || "—"} · ${agent.tools} tool${
110
134
  agent.tools === 1 ? "" : "s"
111
135
  } · ${agent.channels} channel${agent.channels === 1 ? "" : "s"}`;
112
- return ` <a class="card" href="/${escapeHtml(agent.slug)}/playground">
136
+ return ` <a class="card" href="${escapeHtml(playground)}">
113
137
  <div class="card-name">${escapeHtml(agent.name)}</div>
114
- <div class="card-slug">/${escapeHtml(agent.slug)}</div>
138
+ <div class="card-slug">${escapeHtml(slugLabel)} → playground</div>
115
139
  <div class="card-meta">${escapeHtml(meta)}</div>
116
140
  </a>`;
117
141
  })
118
142
  .join("\n");
119
- return AGENTS_INDEX_TEMPLATE.replace(
143
+
144
+ const routes = hostRouteRows(agents, docs, multi)
145
+ .map(
146
+ (row) =>
147
+ ` <tr><td><a href="${escapeHtml(row.href)}"><code>${escapeHtml(row.path)}</code></a></td><td>${escapeHtml(row.blurb)}</td></tr>`
148
+ )
149
+ .join("\n");
150
+
151
+ return HOST_HOME_TEMPLATE.replace(
120
152
  "<!--CARDS-->",
121
153
  cards || ' <div class="card-meta">No agents mounted.</div>'
122
- ).replace("<!--COUNT-->", String(agents.length));
154
+ )
155
+ .replace("<!--COUNT-->", String(agents.length))
156
+ .replace("<!--ROUTES-->", routes);
157
+ }
158
+
159
+ function hostRouteRows(
160
+ agents: AgentIndexEntry[],
161
+ docs: boolean,
162
+ multi: boolean
163
+ ): Array<{ path: string; href: string; blurb: string }> {
164
+ const rows: Array<{ path: string; href: string; blurb: string }> = [
165
+ { path: "/", href: "/", blurb: "This page" },
166
+ ];
167
+ for (const agent of agents) {
168
+ const base = agent.slug === "" ? "" : `/${agent.slug}`;
169
+ const label = agent.slug === "" ? agent.name : agent.slug;
170
+ rows.push({
171
+ path: `${base}/playground`,
172
+ href: `${base}/playground`,
173
+ blurb: `Playground — ${label}`,
174
+ });
175
+ rows.push({
176
+ path: `${base}/v1/health`,
177
+ href: `${base}/v1/health`,
178
+ blurb: `Health — ${label}`,
179
+ });
180
+ }
181
+ if (multi) {
182
+ rows.push({
183
+ path: "/v1/agents",
184
+ href: "/v1/agents",
185
+ blurb: "JSON index of mounted agents",
186
+ });
187
+ rows.push({
188
+ path: "/v1/health",
189
+ href: "/v1/health",
190
+ blurb: "Host liveness",
191
+ });
192
+ }
193
+ if (docs) {
194
+ rows.push({
195
+ path: "/docs",
196
+ href: "/docs/",
197
+ blurb: "Agent SDK documentation",
198
+ });
199
+ }
200
+ return rows;
123
201
  }
124
202
 
125
203
  function escapeHtml(value: string): string {
@@ -137,7 +215,7 @@ function fallbackHtml(baseHref: string): string {
137
215
  <base href="${escapeHtml(baseHref)}" />
138
216
  <meta charset="utf-8" />
139
217
  <meta name="viewport" content="width=device-width, initial-scale=1" />
140
- <title>agent-serve playground</title>
218
+ <title>agent-sdk playground</title>
141
219
  <style>
142
220
  body { margin: 0; background: #0e1116; color: #e6edf3; font: 14px/1.6 ui-sans-serif, system-ui, sans-serif; }
143
221
  .box { max-width: 640px; margin: 64px auto; padding: 0 24px; }
@@ -150,7 +228,7 @@ function fallbackHtml(baseHref: string): string {
150
228
  <div class="box">
151
229
  <h1>Playground bundle not built</h1>
152
230
  <p>
153
- The Vite playground has not been built yet. <code>agent-serve serve</code>
231
+ The Vite playground has not been built yet. <code>agent-sdk serve</code>
154
232
  auto-builds it when the local vite toolchain is available; otherwise run
155
233
  <code>pnpm run build:playground</code> from <code>packages/agent-serve</code>,
156
234
  or <code>pnpm run dev:playground</code> for Vite HMR.
@@ -160,33 +238,46 @@ function fallbackHtml(baseHref: string): string {
160
238
  </html>`;
161
239
  }
162
240
 
163
- const AGENTS_INDEX_TEMPLATE = `<!doctype html>
241
+ const HOST_HOME_TEMPLATE = `<!doctype html>
164
242
  <html lang="en">
165
243
  <head>
166
244
  <meta charset="utf-8" />
167
245
  <meta name="viewport" content="width=device-width, initial-scale=1" />
168
- <title>agent-serve — agents</title>
246
+ <title>Agent SDK</title>
169
247
  <style>
170
248
  :root { color-scheme: dark; }
171
249
  body { margin: 0; background: #0e1116; color: #e6edf3; font: 14px/1.6 ui-sans-serif, system-ui, -apple-system, sans-serif; }
172
- .agents-wrap { max-width: 880px; margin: 0 auto; padding: 48px 24px; }
173
- .agents-wrap h1 { font-size: 20px; font-weight: 650; margin: 0 0 2px; }
174
- .agents-wrap .sub { color: #8b949e; font-size: 13px; margin-bottom: 28px; }
175
- .agents-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
250
+ .wrap { max-width: 880px; margin: 0 auto; padding: 48px 24px; }
251
+ h1 { font-size: 20px; font-weight: 650; margin: 0 0 2px; }
252
+ h2 { font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: #8b949e; margin: 36px 0 12px; }
253
+ .sub { color: #8b949e; font-size: 13px; margin-bottom: 28px; }
254
+ .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
176
255
  .card { display: block; text-decoration: none; color: inherit; background: #161b22; border: 1px solid #30363d; border-radius: 12px; padding: 16px 18px; transition: border-color 0.12s; }
177
256
  .card:hover { border-color: #6cb6ff; }
178
257
  .card-name { font-size: 15px; font-weight: 600; }
179
258
  .card-slug { font-family: ui-monospace, monospace; font-size: 12px; color: #6cb6ff; margin-top: 2px; }
180
259
  .card-meta { color: #6e7681; font-size: 12px; margin-top: 10px; }
260
+ table.routes { width: 100%; border-collapse: collapse; font-size: 13px; }
261
+ table.routes td { padding: 8px 0; border-bottom: 1px solid #21262d; vertical-align: top; }
262
+ table.routes td:first-child { width: 42%; padding-right: 16px; }
263
+ table.routes code { font-family: ui-monospace, monospace; font-size: 12px; color: #6cb6ff; }
264
+ table.routes a { text-decoration: none; }
265
+ table.routes a:hover code { text-decoration: underline; }
266
+ .muted { color: #8b949e; }
181
267
  </style>
182
268
  </head>
183
269
  <body>
184
- <div class="agents-wrap">
185
- <h1>agent-serve</h1>
186
- <div class="sub"><!--COUNT--> agents mounted</div>
187
- <div class="agents-grid">
270
+ <div class="wrap">
271
+ <h1>Agent SDK</h1>
272
+ <div class="sub"><!--COUNT--> agent(s) on this host. Open a playground or use a route below.</div>
273
+ <h2>Agents</h2>
274
+ <div class="grid">
188
275
  <!--CARDS-->
189
276
  </div>
277
+ <h2>Routes</h2>
278
+ <table class="routes">
279
+ <!--ROUTES-->
280
+ </table>
190
281
  </div>
191
282
  </body>
192
283
  </html>`;
@@ -171,7 +171,17 @@ export class CursorSdkRunner implements AgentRunner {
171
171
  const agentPromise = this.getAgent(request);
172
172
  try {
173
173
  const agent = await agentPromise;
174
- const run = await agent.send(request.prompt, {
174
+ const sendMessage =
175
+ request.images !== undefined && request.images.length > 0
176
+ ? {
177
+ text: request.prompt,
178
+ images: request.images.map((image) => ({
179
+ data: image.data,
180
+ mimeType: image.mimeType,
181
+ })),
182
+ }
183
+ : request.prompt;
184
+ const run = await agent.send(sendMessage, {
175
185
  onDelta: async ({ update }) => {
176
186
  try {
177
187
  await request.onUpdate(update);
@@ -58,6 +58,7 @@ import {
58
58
  import { CursorGithubCredentialProvider } from "./cursor/github-credentials.js";
59
59
  import {
60
60
  CursorEventRelay,
61
+ formatCursorScmDispatchOutcome,
61
62
  formatCursorScmEventLog,
62
63
  scmMetadataToGitHubPayload,
63
64
  } from "./cursor-event-relay.js";
@@ -98,6 +99,7 @@ import {
98
99
  requireSessionOwner,
99
100
  } from "./http-channel.js";
100
101
  import { validateToolInput } from "./json-schema.js";
102
+ import { DEFAULT_SERVE_PORT, listenHttpServer } from "./listen-port.js";
101
103
  import {
102
104
  createLocalControlPlane,
103
105
  type LocalControlPlane,
@@ -591,52 +593,26 @@ export async function startServer(
591
593
  })),
592
594
  }),
593
595
  });
596
+ }
594
597
 
595
- if (playground) {
596
- const cursorSlackBySlug = new Map(
597
- collectCursorSlackTargets(runtimes).map((target) => [
598
- target.runtime.slug,
599
- target.agentName,
600
- ])
601
- );
602
- const entries: AgentIndexEntry[] = runtimes.map(({ slug, project }) => {
603
- const cursorName = cursorSlackBySlug.get(slug);
604
- const hasSocketSlack = project.channels.some(
605
- (channel) =>
606
- channel.definition.meta?.kind === "slack" &&
607
- channel.definition.meta?.slackCursorAccount !== true
608
- );
609
- const slack =
610
- cursorName !== undefined
611
- ? {
612
- kind: "cursor-account" as const,
613
- handle: `@Cursor ${cursorName}`,
614
- }
615
- : hasSocketSlack
616
- ? { kind: "socket" as const }
617
- : undefined;
618
- return {
619
- slug,
620
- name: project.name,
621
- model:
622
- typeof project.agent.model === "string"
623
- ? project.agent.model
624
- : (project.agent.model?.id ?? ""),
625
- tools: project.agent.tools.length,
626
- channels: project.channels.length + 1,
627
- ...(slack !== undefined ? { slack } : {}),
628
- };
629
- });
630
- const indexHtml = renderAgentsIndexHtml(entries);
631
- router.add({
632
- method: "GET",
633
- pattern: "/",
634
- auth: "none",
635
- handler: () =>
636
- new Response(indexHtml, {
637
- headers: { "content-type": "text/html; charset=utf-8" },
638
- }),
639
- });
598
+ // Orienting `/` for every layout (single empty-slug and multi).
599
+ // Lists playground links plus live routes (playground, health, docs).
600
+ if (playground) {
601
+ const entries = buildAgentIndexEntries(runtimes);
602
+ const indexHtml = renderAgentsIndexHtml(entries, {
603
+ docs: docsSite,
604
+ multi,
605
+ });
606
+ router.add({
607
+ method: "GET",
608
+ pattern: "/",
609
+ auth: "none",
610
+ handler: () =>
611
+ new Response(indexHtml, {
612
+ headers: { "content-type": "text/html; charset=utf-8" },
613
+ }),
614
+ });
615
+ if (multi) {
640
616
  router.add({
641
617
  method: "GET",
642
618
  pattern: "/v1/agents",
@@ -724,10 +700,17 @@ export async function startServer(
724
700
  });
725
701
 
726
702
  const host = options.host ?? "127.0.0.1";
727
- const port = options.port ?? 3000;
703
+ const portWasExplicit = options.port !== undefined;
704
+ const requestedPort = options.port ?? DEFAULT_SERVE_PORT;
705
+ let boundPort: number;
728
706
  try {
729
- server.listen(port, host);
730
- await once(server, "listening");
707
+ boundPort = await listenHttpServer({
708
+ server,
709
+ host,
710
+ port: requestedPort,
711
+ portWasExplicit,
712
+ logger,
713
+ });
731
714
  } catch (error) {
732
715
  await cursorEventRelay?.stop();
733
716
  await cursorSlackRelay?.stop();
@@ -736,7 +719,7 @@ export async function startServer(
736
719
  }
737
720
  const address = server.address() as AddressInfo;
738
721
  const displayHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
739
- const url = `http://${displayHost}:${address.port}`;
722
+ const url = `http://${displayHost}:${boundPort}`;
740
723
 
741
724
  // Resolve peer + Cursor account MCP connections now that the listen port is
742
725
  // known (see resolved-connections.ts for the URL/runtime rules).
@@ -1302,18 +1285,6 @@ function mountAgentRoutes(
1302
1285
  auth: "none",
1303
1286
  handler: (_request, ctx) => renderPlaygroundAsset(ctx.params.file ?? ""),
1304
1287
  });
1305
- if (!multi) {
1306
- router.add({
1307
- method: "GET",
1308
- pattern: "/",
1309
- auth: "none",
1310
- handler: () =>
1311
- new Response(null, {
1312
- status: 302,
1313
- headers: { location: "/playground" },
1314
- }),
1315
- });
1316
- }
1317
1288
  }
1318
1289
 
1319
1290
  for (const route of buildHttpChannelRoutes({
@@ -1796,8 +1767,14 @@ function startCursorEventRelay(input: {
1796
1767
  targets,
1797
1768
  });
1798
1769
  const outcomes = result.targets
1799
- .map((target) => `${target.slug || "default"}:${target.status}`)
1770
+ .map((target) => formatCursorScmDispatchOutcome(target))
1800
1771
  .join(", ");
1772
+ const rejected = result.targets.filter((target) => target.status >= 400);
1773
+ if (rejected.length > 0) {
1774
+ logger(
1775
+ `[agent-sdk] cursor-events: dropped ${formatCursorScmEventLog(event)} before hook: ${rejected.map(formatCursorScmDispatchOutcome).join(", ")}`
1776
+ );
1777
+ }
1801
1778
  logger(
1802
1779
  `[agent-sdk] cursor-events: dispatched ${formatCursorScmEventLog(event)}${outcomes === "" ? "" : ` → ${outcomes}`}`
1803
1780
  );
@@ -1893,6 +1870,43 @@ function slackAgentNameFromSlug(slug: string): string {
1893
1870
  * resolved. Duplicate names are a startup error — the backend routes
1894
1871
  * `@Cursor <name> …` by name, so two mounts cannot share one.
1895
1872
  */
1873
+ function buildAgentIndexEntries(runtimes: MountedRuntime[]): AgentIndexEntry[] {
1874
+ const cursorSlackBySlug = new Map(
1875
+ collectCursorSlackTargets(runtimes).map((target) => [
1876
+ target.runtime.slug,
1877
+ target.agentName,
1878
+ ])
1879
+ );
1880
+ return runtimes.map(({ slug, project }) => {
1881
+ const cursorName = cursorSlackBySlug.get(slug);
1882
+ const hasSocketSlack = project.channels.some(
1883
+ (channel) =>
1884
+ channel.definition.meta?.kind === "slack" &&
1885
+ channel.definition.meta?.slackCursorAccount !== true
1886
+ );
1887
+ const slack =
1888
+ cursorName !== undefined
1889
+ ? {
1890
+ kind: "cursor-account" as const,
1891
+ handle: `@Cursor ${cursorName}`,
1892
+ }
1893
+ : hasSocketSlack
1894
+ ? { kind: "socket" as const }
1895
+ : undefined;
1896
+ return {
1897
+ slug,
1898
+ name: project.name,
1899
+ model:
1900
+ typeof project.agent.model === "string"
1901
+ ? project.agent.model
1902
+ : (project.agent.model?.id ?? ""),
1903
+ tools: project.agent.tools.length,
1904
+ channels: project.channels.length + 1,
1905
+ slack,
1906
+ };
1907
+ });
1908
+ }
1909
+
1896
1910
  function collectCursorSlackTargets(
1897
1911
  runtimes: MountedRuntime[]
1898
1912
  ): CursorSlackTarget[] {