@cursor/july 0.1.26 → 0.1.27

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 (277) hide show
  1. package/dist/ab.d.ts +6 -0
  2. package/dist/ab.d.ts.map +1 -1
  3. package/dist/ab.js +1 -0
  4. package/dist/artifacts.d.ts +34 -0
  5. package/dist/artifacts.d.ts.map +1 -0
  6. package/dist/artifacts.js +57 -0
  7. package/dist/bin/agent-serve.js +31 -9
  8. package/dist/channels/github/github-channel.d.ts +1 -1
  9. package/dist/channels/github/github-channel.d.ts.map +1 -1
  10. package/dist/channels/github/github-channel.js +14 -1
  11. package/dist/channels/github/types.d.ts +6 -1
  12. package/dist/channels/github/types.d.ts.map +1 -1
  13. package/dist/channels/slack/api.d.ts +2 -1
  14. package/dist/channels/slack/api.d.ts.map +1 -1
  15. package/dist/channels/slack/api.js +1 -0
  16. package/dist/channels/slack/cursor-account.d.ts +2 -1
  17. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  18. package/dist/channels/slack/cursor-account.js +1 -1
  19. package/dist/channels/slack/dispatch.d.ts +4 -1
  20. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  21. package/dist/channels/slack/dispatch.js +3 -3
  22. package/dist/channels/slack/interactive.d.ts +2 -1
  23. package/dist/channels/slack/interactive.d.ts.map +1 -1
  24. package/dist/channels/slack/interactive.js +1 -0
  25. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  26. package/dist/channels/slack/slack-channel.js +4 -3
  27. package/dist/channels/slack/types.d.ts +6 -1
  28. package/dist/channels/slack/types.d.ts.map +1 -1
  29. package/dist/docs/404.html +1 -1
  30. package/dist/docs/ab.html +2 -2
  31. package/dist/docs/assets/{app.DYcC9FY-.js → app.BICLoJYF.js} +1 -1
  32. package/dist/docs/assets/chunks/@localSearchIndexroot.CQJytDwU.js +1 -0
  33. package/dist/docs/assets/chunks/{VPLocalSearchBox.o4N_knTV.js → VPLocalSearchBox.wN4Ko9dT.js} +1 -1
  34. package/dist/docs/assets/chunks/{theme.DQ-njyo0.js → theme.dnnTE-Co.js} +2 -2
  35. package/dist/docs/building-with-agents.html +2 -2
  36. package/dist/docs/concepts.html +2 -2
  37. package/dist/docs/deployment.html +2 -2
  38. package/dist/docs/evals.html +2 -2
  39. package/dist/docs/example-agents/approval-buddy.html +2 -2
  40. package/dist/docs/example-agents/benny.html +2 -2
  41. package/dist/docs/example-agents/bugbot.html +2 -2
  42. package/dist/docs/example-agents/codebase-wiki.html +2 -2
  43. package/dist/docs/example-agents/codeowners-review.html +2 -2
  44. package/dist/docs/example-agents/concierge.html +2 -2
  45. package/dist/docs/example-agents/fsd.html +2 -2
  46. package/dist/docs/example-agents/index.html +2 -2
  47. package/dist/docs/example-agents/knowledge-base.html +2 -2
  48. package/dist/docs/example-agents/oncall.html +2 -2
  49. package/dist/docs/example-agents/security-reviewer.html +2 -2
  50. package/dist/docs/example-agents/slack-agent.html +2 -2
  51. package/dist/docs/example-agents/weather-agent.html +2 -2
  52. package/dist/docs/guides/agent-to-agent.html +2 -2
  53. package/dist/docs/guides/cloud-runtime.html +2 -2
  54. package/dist/docs/guides/github.html +2 -2
  55. package/dist/docs/guides/human-in-the-loop.html +2 -2
  56. package/dist/docs/guides/mcp-oauth.html +2 -2
  57. package/dist/docs/guides/slack.html +2 -2
  58. package/dist/docs/guides/webhooks.html +2 -2
  59. package/dist/docs/hillclimbing.html +2 -2
  60. package/dist/docs/index.html +2 -2
  61. package/dist/docs/quickstart.html +2 -2
  62. package/dist/docs/reference/agent-config.html +2 -2
  63. package/dist/docs/reference/channels.html +2 -2
  64. package/dist/docs/reference/cli.html +2 -2
  65. package/dist/docs/reference/connections.html +2 -2
  66. package/dist/docs/reference/hooks.html +2 -2
  67. package/dist/docs/reference/http-api.html +2 -2
  68. package/dist/docs/reference/instructions.html +2 -2
  69. package/dist/docs/reference/playground.html +2 -2
  70. package/dist/docs/reference/project-layout.html +2 -2
  71. package/dist/docs/reference/prompt.html +2 -2
  72. package/dist/docs/reference/schedules.html +2 -2
  73. package/dist/docs/reference/sessions.html +2 -2
  74. package/dist/docs/reference/skills.html +2 -2
  75. package/dist/docs/reference/subagents.html +2 -2
  76. package/dist/docs/reference/tools.html +2 -2
  77. package/dist/docs/scaffolding-agents.html +2 -2
  78. package/dist/docs/storage.html +2 -2
  79. package/dist/docs/troubleshooting.html +2 -2
  80. package/dist/evals/assertions.d.ts +10 -1
  81. package/dist/evals/assertions.d.ts.map +1 -1
  82. package/dist/evals/assertions.js +12 -0
  83. package/dist/evals.d.ts +1 -1
  84. package/dist/evals.d.ts.map +1 -1
  85. package/dist/index.d.ts +1 -0
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +1 -0
  88. package/dist/internal/ab-fold.d.ts.map +1 -1
  89. package/dist/internal/ab-fold.js +4 -0
  90. package/dist/internal/ab-snapshot.d.ts.map +1 -1
  91. package/dist/internal/ab-snapshot.js +1 -0
  92. package/dist/internal/artifacts-store.d.ts +45 -0
  93. package/dist/internal/artifacts-store.d.ts.map +1 -0
  94. package/dist/internal/artifacts-store.js +250 -0
  95. package/dist/internal/builtin-tools/artifacts.d.ts +10 -0
  96. package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -0
  97. package/dist/internal/builtin-tools/artifacts.js +73 -0
  98. package/dist/internal/builtin-tools/index.d.ts +8 -1
  99. package/dist/internal/builtin-tools/index.d.ts.map +1 -1
  100. package/dist/internal/builtin-tools/index.js +15 -0
  101. package/dist/internal/cli-ax.d.ts +11 -0
  102. package/dist/internal/cli-ax.d.ts.map +1 -1
  103. package/dist/internal/cli-ax.js +80 -0
  104. package/dist/internal/cli-slack.d.ts.map +1 -1
  105. package/dist/internal/cli-slack.js +14 -6
  106. package/dist/internal/discovery.d.ts.map +1 -1
  107. package/dist/internal/discovery.js +83 -2
  108. package/dist/internal/distribution.d.ts.map +1 -1
  109. package/dist/internal/distribution.js +1 -0
  110. package/dist/internal/model-pricing.d.ts +49 -0
  111. package/dist/internal/model-pricing.d.ts.map +1 -0
  112. package/dist/internal/model-pricing.js +377 -0
  113. package/dist/internal/reminder-runner.d.ts.map +1 -1
  114. package/dist/internal/reminder-runner.js +1 -0
  115. package/dist/internal/schedule-runner.d.ts.map +1 -1
  116. package/dist/internal/schedule-runner.js +1 -0
  117. package/dist/internal/sdk-runner.d.ts.map +1 -1
  118. package/dist/internal/sdk-runner.js +9 -1
  119. package/dist/internal/server.d.ts.map +1 -1
  120. package/dist/internal/server.js +39 -0
  121. package/dist/internal/session-cost.d.ts +49 -0
  122. package/dist/internal/session-cost.d.ts.map +1 -0
  123. package/dist/internal/session-cost.js +175 -0
  124. package/dist/internal/session-engine.d.ts +35 -1
  125. package/dist/internal/session-engine.d.ts.map +1 -1
  126. package/dist/internal/session-engine.js +128 -7
  127. package/dist/internal/storage-coordinator.d.ts +15 -0
  128. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  129. package/dist/internal/storage-coordinator.js +52 -0
  130. package/dist/internal/trajectory.d.ts +9 -1
  131. package/dist/internal/trajectory.d.ts.map +1 -1
  132. package/dist/internal/trajectory.js +32 -5
  133. package/dist/playground/assets/index-BDCeW4uc.js +86 -0
  134. package/dist/playground/assets/index-Bul1WfrB.css +1 -0
  135. package/dist/playground/index.html +2 -2
  136. package/dist/storage.d.ts +4 -0
  137. package/dist/storage.d.ts.map +1 -1
  138. package/dist/storage.js +4 -0
  139. package/dist/types.d.ts +164 -1
  140. package/dist/types.d.ts.map +1 -1
  141. package/package.json +26 -26
  142. package/src/ab.ts +7 -0
  143. package/src/bin/agent-serve.ts +30 -2
  144. package/src/internal/ab-fold.ts +4 -0
  145. package/src/internal/ab-snapshot.ts +1 -0
  146. package/src/internal/cli-ax.ts +95 -0
  147. package/src/internal/model-pricing.ts +426 -0
  148. package/src/internal/sdk-runner.ts +10 -2
  149. package/src/internal/session-cost.ts +231 -0
  150. package/src/internal/session-engine.ts +28 -0
  151. package/src/internal/trajectory.ts +45 -3
  152. package/src/types.ts +24 -1
  153. package/dist/channels/github/instrument.d.ts +0 -20
  154. package/dist/channels/github/instrument.d.ts.map +0 -1
  155. package/dist/docs/assets/chunks/@localSearchIndexroot.BQTzJjR_.js +0 -1
  156. package/dist/internal/json-dir-store.d.ts +0 -32
  157. package/dist/internal/json-dir-store.d.ts.map +0 -1
  158. package/dist/internal/json-dir-store.js +0 -100
  159. package/dist/playground/assets/index-CjOQ4hN9.css +0 -1
  160. package/dist/playground/assets/index-DqXdAFGa.js +0 -85
  161. package/src/bin/agent-serve.version.test.ts +0 -64
  162. package/src/channels/github/api.test.ts +0 -64
  163. package/src/channels/github/auth.test.ts +0 -105
  164. package/src/channels/github/cursor-account.test.ts +0 -204
  165. package/src/channels/github/forward.test.ts +0 -457
  166. package/src/channels/github/github.test.ts +0 -985
  167. package/src/channels/github/replay.test.ts +0 -179
  168. package/src/channels/slack/api.post-message.test.ts +0 -148
  169. package/src/channels/slack/approvals.test.ts +0 -328
  170. package/src/channels/slack/block-actions.test.ts +0 -469
  171. package/src/channels/slack/bot-mentions.test.ts +0 -267
  172. package/src/channels/slack/channel-watch.test.ts +0 -363
  173. package/src/channels/slack/cursor-account.test.ts +0 -301
  174. package/src/channels/slack/defaults.final-post.test.ts +0 -182
  175. package/src/channels/slack/dispatch.test.ts +0 -875
  176. package/src/channels/slack/eval-directive.test.ts +0 -273
  177. package/src/channels/slack/message-body.test.ts +0 -54
  178. package/src/channels/slack/nudge-store.test.ts +0 -143
  179. package/src/channels/slack/slack.test.ts +0 -391
  180. package/src/channels/slack/stop.test.ts +0 -23
  181. package/src/channels/slack/thread-context.test.ts +0 -202
  182. package/src/evals/assertions.test.ts +0 -617
  183. package/src/evals/expect.test.ts +0 -144
  184. package/src/evals/judge.test.ts +0 -181
  185. package/src/evals/loaders.test.ts +0 -132
  186. package/src/evals/matchers.test.ts +0 -95
  187. package/src/evals/reporters.test.ts +0 -303
  188. package/src/evals/run-facts.test.ts +0 -259
  189. package/src/internal/ab-snapshot.test.ts +0 -325
  190. package/src/internal/approval-gate.test.ts +0 -49
  191. package/src/internal/approvals.integration.test.ts +0 -383
  192. package/src/internal/artifacts-store.test.ts +0 -283
  193. package/src/internal/authored-loaders.test.ts +0 -31
  194. package/src/internal/builtin-tools/artifacts.test.ts +0 -247
  195. package/src/internal/builtin-tools/reminders.test.ts +0 -201
  196. package/src/internal/channel-route-schema.test.ts +0 -294
  197. package/src/internal/chat-attach.test.ts +0 -262
  198. package/src/internal/cli-deploy.test.ts +0 -1991
  199. package/src/internal/cli-docs.test.ts +0 -161
  200. package/src/internal/cli-mcp.test.ts +0 -789
  201. package/src/internal/cli-skills.test.ts +0 -133
  202. package/src/internal/cli-slack.test.ts +0 -1654
  203. package/src/internal/cloud-merge.test.ts +0 -74
  204. package/src/internal/cron.test.ts +0 -22
  205. package/src/internal/cursor/account-mcp.test.ts +0 -807
  206. package/src/internal/cursor/backend-client.test.ts +0 -591
  207. package/src/internal/cursor/credentials.test.ts +0 -351
  208. package/src/internal/cursor/github-credentials.test.ts +0 -136
  209. package/src/internal/cursor-account-mcp-auth.test.ts +0 -310
  210. package/src/internal/cursor-account.integration.test.ts +0 -441
  211. package/src/internal/cursor-event-relay.test.ts +0 -746
  212. package/src/internal/cursor-github-credentials.integration.test.ts +0 -271
  213. package/src/internal/cursor-slack-relay.test.ts +0 -525
  214. package/src/internal/deploy-source.test.ts +0 -111
  215. package/src/internal/discovery.artifact-tool.test.ts +0 -136
  216. package/src/internal/discovery.artifacts.test.ts +0 -119
  217. package/src/internal/discovery.builtin-tools.test.ts +0 -94
  218. package/src/internal/discovery.concurrency.test.ts +0 -60
  219. package/src/internal/discovery.cursor-account.test.ts +0 -133
  220. package/src/internal/discovery.cwd.test.ts +0 -83
  221. package/src/internal/discovery.hosting.test.ts +0 -80
  222. package/src/internal/discovery.identity.test.ts +0 -44
  223. package/src/internal/docs-site.test.ts +0 -66
  224. package/src/internal/duration.test.ts +0 -29
  225. package/src/internal/eval-judge-model.test.ts +0 -187
  226. package/src/internal/eval-run-store.cancel.test.ts +0 -142
  227. package/src/internal/eval-run-store.storage.test.ts +0 -211
  228. package/src/internal/eval-runner.http.test.ts +0 -403
  229. package/src/internal/eval-runner.run.test.ts +0 -928
  230. package/src/internal/evals-client.test.ts +0 -307
  231. package/src/internal/event-mapper.test.ts +0 -243
  232. package/src/internal/github-fanout.test.ts +0 -213
  233. package/src/internal/handleAgentServeTrigger.test.ts +0 -185
  234. package/src/internal/host-kv.test.ts +0 -82
  235. package/src/internal/host-platforms.test.ts +0 -126
  236. package/src/internal/http-channel.test.ts +0 -402
  237. package/src/internal/init-project.test.ts +0 -270
  238. package/src/internal/install-cursor-skills.test.ts +0 -262
  239. package/src/internal/local-env.test.ts +0 -120
  240. package/src/internal/log-ring.test.ts +0 -31
  241. package/src/internal/logs-client.test.ts +0 -350
  242. package/src/internal/mcp-endpoint.test.ts +0 -436
  243. package/src/internal/mcp-host.test.ts +0 -298
  244. package/src/internal/mcp-oauth.test.ts +0 -148
  245. package/src/internal/net.test.ts +0 -17
  246. package/src/internal/peer-connections.test.ts +0 -128
  247. package/src/internal/peer-mcp.integration.test.ts +0 -289
  248. package/src/internal/playground/toolchain.test.ts +0 -53
  249. package/src/internal/playground-cli.test.ts +0 -187
  250. package/src/internal/playground-proxy.test.ts +0 -376
  251. package/src/internal/prompt-context.integration.test.ts +0 -232
  252. package/src/internal/prompt-context.test.ts +0 -127
  253. package/src/internal/reminder-runner.test.ts +0 -432
  254. package/src/internal/reminder-store.test.ts +0 -53
  255. package/src/internal/request-headers.test.ts +0 -27
  256. package/src/internal/resolve-prod-target.test.ts +0 -787
  257. package/src/internal/resolved-connections.test.ts +0 -295
  258. package/src/internal/router.test.ts +0 -57
  259. package/src/internal/sdk-runner.test.ts +0 -290
  260. package/src/internal/server.artifacts.test.ts +0 -334
  261. package/src/internal/session-engine.artifacts.test.ts +0 -293
  262. package/src/internal/session-engine.coalesce.test.ts +0 -169
  263. package/src/internal/session-engine.concurrency.test.ts +0 -250
  264. package/src/internal/session-engine.host-oauth-mcp.test.ts +0 -110
  265. package/src/internal/session-engine.interrupt.test.ts +0 -577
  266. package/src/internal/session-engine.storage.test.ts +0 -547
  267. package/src/internal/session-urls.test.ts +0 -28
  268. package/src/internal/sessions-client.test.ts +0 -518
  269. package/src/internal/storage-coordinator.test.ts +0 -517
  270. package/src/internal/tool-call.test.ts +0 -458
  271. package/src/internal/tool-result.test.ts +0 -52
  272. package/src/internal/trajectory.approvals.test.ts +0 -83
  273. package/src/internal/trajectory.subagents.test.ts +0 -198
  274. package/src/internal/turn-governor.test.ts +0 -137
  275. package/src/internal/update-check.test.ts +0 -485
  276. package/src/internal/workspace.test.ts +0 -207
  277. package/src/storage-backends/cursor-hosted.test.ts +0 -121
@@ -29,6 +29,7 @@ import {
29
29
  type AxCliOptions,
30
30
  cmdCall,
31
31
  cmdChat,
32
+ cmdCost,
32
33
  cmdEval,
33
34
  cmdEvalCancel,
34
35
  cmdEvalStatus,
@@ -55,6 +56,7 @@ import {
55
56
  } from "../internal/cli-deploy.js";
56
57
  import {
57
58
  CLI_COMMAND_NAME as CLI,
59
+ PACKAGE_NAME,
58
60
  packageVersion,
59
61
  } from "../internal/distribution.js";
60
62
  import { loadAgentLocalEnv } from "../internal/local-env.js";
@@ -85,6 +87,8 @@ Usage:
85
87
  ${CLI} session <sessionId> [--url <baseUrl> | --prod]
86
88
  [--json | --text | --events]
87
89
  [--slug <slug>] [--team <id>] [--dir <path>]
90
+ ${CLI} cost [sessionId] [--url <baseUrl> | --prod] [--json]
91
+ [--slug <slug>] [--team <id>] [--dir <path>]
88
92
  ${CLI} playground [--url <baseUrl> | --prod] [--session <id>] [--print]
89
93
  [--slug <slug>] [--team <id>] [--dir <path>]
90
94
  ${CLI} docs [--port <n>] [--host <host>] [--print]
@@ -161,6 +165,10 @@ Commands:
161
165
  session Show one session. Default: text trajectory. --json for trajectory
162
166
  JSON; --events for the raw event list. To continue interactively,
163
167
  use \`${CLI} resume [id]\`.
168
+ cost Pull cost metrics. With a session id: per-turn token usage and
169
+ estimated cost for that session. Without one: one row per
170
+ session plus a total. Costs are the engine's recorded estimates
171
+ from turn.completed events; --json for machine-readable reports.
164
172
  playground Open the web playground. --prod starts a loopback proxy that
165
173
  injects hosted engine credentials (browsers cannot set them).
166
174
  --print skips opening a browser. Ctrl-C stops the proxy.
@@ -908,6 +916,22 @@ async function main(argv: string[]): Promise<number> {
908
916
  );
909
917
  }
910
918
 
919
+ case "cost": {
920
+ return cmdCost(
921
+ positionals[0] ?? values["session-id"] ?? values.session,
922
+ axFromOptions({
923
+ dir,
924
+ url: values.url,
925
+ prod: values.prod,
926
+ team: values.team,
927
+ apiKey: values["api-key"],
928
+ slug: values.slug,
929
+ bearerToken: values["bearer-token"],
930
+ json: values.json,
931
+ })
932
+ );
933
+ }
934
+
911
935
  case "playground": {
912
936
  return cmdPlayground(
913
937
  axFromOptions({
@@ -1383,11 +1407,15 @@ function printServeBanner(
1383
1407
  ): void {
1384
1408
  const c = stdoutPalette();
1385
1409
  const agents = handle.agents;
1410
+ // Name the framework build in the boot line: hosted engines install the
1411
+ // published package at provision time, so the serve log is the only
1412
+ // durable record of which build a deployment's turns actually ran on.
1413
+ const versionTag = c.dim(`(${PACKAGE_NAME} ${packageVersion()})`);
1386
1414
  if (agents.length > 1) {
1387
- process.stdout.write(`${agents.length} agents listening\n`);
1415
+ process.stdout.write(`${agents.length} agents listening ${versionTag}\n`);
1388
1416
  } else {
1389
1417
  const info = projectInfo(agents[0]?.project ?? handle.project);
1390
- process.stdout.write(`${info.name} listening\n`);
1418
+ process.stdout.write(`${info.name} listening ${versionTag}\n`);
1391
1419
  }
1392
1420
 
1393
1421
  const playgroundUrl = resolvePlaygroundBannerUrl(handle, opts.noPlayground);
@@ -187,6 +187,10 @@ export function applyBuiltinMetrics(
187
187
  m.cacheReadTokens += usage.cacheReadTokens;
188
188
  m.cacheWriteTokens += usage.cacheWriteTokens;
189
189
  }
190
+ const cost = event.data.cost;
191
+ if (cost !== undefined) {
192
+ m.costUsd += cost.totalUsd;
193
+ }
190
194
  addWallTime(accum, event.at);
191
195
  break;
192
196
  }
@@ -323,6 +323,7 @@ function addMetrics(target: ABMetrics, delta: ABMetrics): void {
323
323
  target.outputTokens += delta.outputTokens;
324
324
  target.cacheReadTokens += delta.cacheReadTokens;
325
325
  target.cacheWriteTokens += delta.cacheWriteTokens;
326
+ target.costUsd += delta.costUsd;
326
327
  target.wallTimeMs += delta.wallTimeMs;
327
328
  for (const [key, value] of Object.entries(delta.custom)) {
328
329
  target.custom[key] = (target.custom[key] ?? 0) + value;
@@ -50,6 +50,7 @@ import {
50
50
  } from "./init-project.js";
51
51
  import { offerInstallCursorSkills } from "./install-cursor-skills.js";
52
52
  import { runLogsTail, sanitizeCustomerError } from "./logs-client.js";
53
+ import { mapPool } from "./map-pool.js";
53
54
  import { openBrowser } from "./open-browser.js";
54
55
  import { startPlaygroundProxy } from "./playground-proxy.js";
55
56
  import {
@@ -59,6 +60,12 @@ import {
59
60
  resolveProdTarget,
60
61
  } from "./resolve-prod-target.js";
61
62
  import { runSession } from "./run-client.js";
63
+ import {
64
+ buildSessionCostReport,
65
+ formatSessionCostText,
66
+ formatSessionsCostText,
67
+ type SessionCostRow,
68
+ } from "./session-cost.js";
62
69
  import {
63
70
  fetchSessionEvents,
64
71
  formatSessionsTable,
@@ -1771,6 +1778,94 @@ export async function cmdSession(
1771
1778
  }
1772
1779
  }
1773
1780
 
1781
+ /** Sessions fetched concurrently when `cost` aggregates every session. */
1782
+ const COST_FETCH_CONCURRENCY = 4;
1783
+
1784
+ /**
1785
+ * `cost [sessionId]` — pull per-session cost metrics.
1786
+ *
1787
+ * With a session id: per-turn token usage + estimated cost for that session.
1788
+ * Without one: one row per session (all sessions on the target) plus a
1789
+ * total row. `--json` prints the underlying report(s); text is the default.
1790
+ * Costs are the engine's recorded estimates on `turn.completed` events
1791
+ * (see `model-pricing.ts`); turns persisted before cost tracking simply
1792
+ * count as unpriced.
1793
+ */
1794
+ export async function cmdCost(
1795
+ sessionId: string | undefined,
1796
+ options: AxCliOptions
1797
+ ): Promise<number> {
1798
+ const target = await resolveSessionTarget(options);
1799
+ if (typeof target === "number") {
1800
+ return target;
1801
+ }
1802
+ const auth = {
1803
+ bearerToken: options.bearerToken,
1804
+ headers: target.headers,
1805
+ };
1806
+
1807
+ try {
1808
+ const resolved = trimNonEmpty(sessionId);
1809
+ if (resolved !== undefined) {
1810
+ const events = await fetchSessionEvents({
1811
+ baseUrl: target.agentUrl,
1812
+ sessionId: resolved,
1813
+ auth,
1814
+ fetchImpl: options.fetchImpl,
1815
+ });
1816
+ const report = buildSessionCostReport(events);
1817
+ if (report.sessionId === "") {
1818
+ report.sessionId = resolved;
1819
+ }
1820
+ if (options.json === true) {
1821
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
1822
+ } else {
1823
+ process.stdout.write(formatSessionCostText(report));
1824
+ }
1825
+ return 0;
1826
+ }
1827
+
1828
+ const listed = await listSessionsWithSource({
1829
+ baseUrl: target.agentUrl,
1830
+ auth,
1831
+ fetchImpl: options.fetchImpl,
1832
+ });
1833
+ const rows = await mapPool(
1834
+ listed.sessions,
1835
+ COST_FETCH_CONCURRENCY,
1836
+ async (session): Promise<SessionCostRow> => {
1837
+ const events = await fetchSessionEvents({
1838
+ baseUrl: target.agentUrl,
1839
+ sessionId: session.sessionId,
1840
+ auth,
1841
+ fetchImpl: options.fetchImpl,
1842
+ });
1843
+ const report = buildSessionCostReport(events);
1844
+ return {
1845
+ sessionId: session.sessionId,
1846
+ channelId: session.channelId,
1847
+ title: session.title,
1848
+ updatedAt: session.updatedAt,
1849
+ totals: report.totals,
1850
+ };
1851
+ }
1852
+ );
1853
+ if (options.json === true) {
1854
+ process.stdout.write(`${JSON.stringify({ sessions: rows }, null, 2)}\n`);
1855
+ } else {
1856
+ process.stdout.write(formatSessionsCostText(rows));
1857
+ }
1858
+ return 0;
1859
+ } catch (error) {
1860
+ process.stderr.write(
1861
+ `${sanitizeCustomerError(
1862
+ error instanceof Error ? error.message : String(error)
1863
+ )}\n`
1864
+ );
1865
+ return 1;
1866
+ }
1867
+ }
1868
+
1774
1869
  /**
1775
1870
  * `playground` — open the web UI against local / `--url` / `--prod`.
1776
1871
  *
@@ -0,0 +1,426 @@
1
+ /**
2
+ * Best-effort dollar-cost estimation for agent turns.
3
+ *
4
+ * The Cursor harness streams token usage (`turn-ended`) but no dollar cost,
5
+ * and server-billed cost is only queryable for cloud agents — so the
6
+ * framework prices token usage against a built-in snapshot of Cursor's
7
+ * published per-model API rates. Estimates are marked `source: "estimate"`
8
+ * everywhere they surface (traces, playground, A/B metrics, CLI); the billed
9
+ * amount on the Cursor dashboard remains the source of truth.
10
+ *
11
+ * Unknown models produce no estimate rather than a wrong one. Deployments
12
+ * can extend or correct the table without a package upgrade via the
13
+ * `AGENT_SERVE_MODEL_PRICING` environment variable (a JSON object merged
14
+ * over the defaults, keyed by model id).
15
+ */
16
+
17
+ import type { ModelSetting, TurnCost, TurnUsage } from "../types.js";
18
+
19
+ /** USD per million tokens for one pricing tier. */
20
+ export interface ModelPricingTier {
21
+ inputUsdPerMTok: number;
22
+ outputUsdPerMTok: number;
23
+ cacheReadUsdPerMTok: number;
24
+ /** Defaults to {@link inputUsdPerMTok} when omitted (provider convention). */
25
+ cacheWriteUsdPerMTok?: number;
26
+ }
27
+
28
+ export interface ModelPricing extends ModelPricingTier {
29
+ /** Rates when the model's `fast` parameter is `"true"`. */
30
+ fast?: ModelPricingTier;
31
+ }
32
+
33
+ const M = 1_000_000;
34
+
35
+ /**
36
+ * Snapshot of Cursor API per-model token rates (USD per million tokens).
37
+ * Keyed by normalized model id; family aliases (`sonnet`, `gpt`, …) point at
38
+ * the model they currently resolve to.
39
+ */
40
+ export const DEFAULT_MODEL_PRICING: Readonly<Record<string, ModelPricing>> = {
41
+ // xAI / Cursor-served Grok
42
+ "grok-4.5": {
43
+ inputUsdPerMTok: 2,
44
+ outputUsdPerMTok: 6,
45
+ cacheReadUsdPerMTok: 0.5,
46
+ fast: { inputUsdPerMTok: 4, outputUsdPerMTok: 18, cacheReadUsdPerMTok: 1 },
47
+ },
48
+ "grok-4.20": {
49
+ inputUsdPerMTok: 2,
50
+ outputUsdPerMTok: 6,
51
+ cacheReadUsdPerMTok: 0.2,
52
+ },
53
+ "grok-4.3": {
54
+ inputUsdPerMTok: 1.25,
55
+ outputUsdPerMTok: 2.5,
56
+ cacheReadUsdPerMTok: 0.125,
57
+ },
58
+ "grok-code": {
59
+ inputUsdPerMTok: 0.2,
60
+ outputUsdPerMTok: 1.5,
61
+ cacheReadUsdPerMTok: 0.02,
62
+ },
63
+
64
+ // Anthropic
65
+ "opus-5": {
66
+ inputUsdPerMTok: 5,
67
+ outputUsdPerMTok: 25,
68
+ cacheReadUsdPerMTok: 0.5,
69
+ cacheWriteUsdPerMTok: 6.25,
70
+ fast: {
71
+ inputUsdPerMTok: 10,
72
+ outputUsdPerMTok: 50,
73
+ cacheReadUsdPerMTok: 1,
74
+ cacheWriteUsdPerMTok: 12.5,
75
+ },
76
+ },
77
+ "opus-4.8": {
78
+ inputUsdPerMTok: 5,
79
+ outputUsdPerMTok: 25,
80
+ cacheReadUsdPerMTok: 0.5,
81
+ cacheWriteUsdPerMTok: 6.25,
82
+ fast: {
83
+ inputUsdPerMTok: 10,
84
+ outputUsdPerMTok: 50,
85
+ cacheReadUsdPerMTok: 1,
86
+ cacheWriteUsdPerMTok: 12.5,
87
+ },
88
+ },
89
+ "opus-4.7": {
90
+ inputUsdPerMTok: 5,
91
+ outputUsdPerMTok: 25,
92
+ cacheReadUsdPerMTok: 0.5,
93
+ cacheWriteUsdPerMTok: 6.25,
94
+ fast: {
95
+ inputUsdPerMTok: 30,
96
+ outputUsdPerMTok: 150,
97
+ cacheReadUsdPerMTok: 3,
98
+ cacheWriteUsdPerMTok: 37.5,
99
+ },
100
+ },
101
+ "opus-4.6": {
102
+ inputUsdPerMTok: 5,
103
+ outputUsdPerMTok: 25,
104
+ cacheReadUsdPerMTok: 0.5,
105
+ cacheWriteUsdPerMTok: 6.25,
106
+ },
107
+ "opus-4.5": {
108
+ inputUsdPerMTok: 5,
109
+ outputUsdPerMTok: 25,
110
+ cacheReadUsdPerMTok: 0.5,
111
+ cacheWriteUsdPerMTok: 6.25,
112
+ },
113
+ "sonnet-5": {
114
+ inputUsdPerMTok: 3,
115
+ outputUsdPerMTok: 15,
116
+ cacheReadUsdPerMTok: 0.3,
117
+ cacheWriteUsdPerMTok: 3.75,
118
+ },
119
+ "sonnet-4.6": {
120
+ inputUsdPerMTok: 3,
121
+ outputUsdPerMTok: 15,
122
+ cacheReadUsdPerMTok: 0.3,
123
+ cacheWriteUsdPerMTok: 3.75,
124
+ },
125
+ "sonnet-4.5": {
126
+ inputUsdPerMTok: 3,
127
+ outputUsdPerMTok: 15,
128
+ cacheReadUsdPerMTok: 0.3,
129
+ cacheWriteUsdPerMTok: 3.75,
130
+ },
131
+ "haiku-4.5": {
132
+ inputUsdPerMTok: 1,
133
+ outputUsdPerMTok: 5,
134
+ cacheReadUsdPerMTok: 0.1,
135
+ cacheWriteUsdPerMTok: 1.25,
136
+ },
137
+ "fable-5": {
138
+ inputUsdPerMTok: 10,
139
+ outputUsdPerMTok: 50,
140
+ cacheReadUsdPerMTok: 1,
141
+ cacheWriteUsdPerMTok: 12.5,
142
+ },
143
+
144
+ // OpenAI
145
+ "gpt-5.6": {
146
+ inputUsdPerMTok: 5,
147
+ outputUsdPerMTok: 30,
148
+ cacheReadUsdPerMTok: 0.5,
149
+ cacheWriteUsdPerMTok: 6.25,
150
+ fast: {
151
+ inputUsdPerMTok: 10,
152
+ outputUsdPerMTok: 60,
153
+ cacheReadUsdPerMTok: 1,
154
+ cacheWriteUsdPerMTok: 12.5,
155
+ },
156
+ },
157
+ "gpt-5.5": {
158
+ inputUsdPerMTok: 5,
159
+ outputUsdPerMTok: 30,
160
+ cacheReadUsdPerMTok: 0.5,
161
+ },
162
+ "gpt-5.4": {
163
+ inputUsdPerMTok: 2.5,
164
+ outputUsdPerMTok: 15,
165
+ cacheReadUsdPerMTok: 0.25,
166
+ },
167
+ "gpt-5.2": {
168
+ inputUsdPerMTok: 1.75,
169
+ outputUsdPerMTok: 14,
170
+ cacheReadUsdPerMTok: 0.175,
171
+ },
172
+ "gpt-mini": {
173
+ inputUsdPerMTok: 0.75,
174
+ outputUsdPerMTok: 4.5,
175
+ cacheReadUsdPerMTok: 0.075,
176
+ },
177
+ "codex-5.3": {
178
+ inputUsdPerMTok: 1.75,
179
+ outputUsdPerMTok: 14,
180
+ cacheReadUsdPerMTok: 0.175,
181
+ },
182
+ "codex-5.2": {
183
+ inputUsdPerMTok: 1.75,
184
+ outputUsdPerMTok: 14,
185
+ cacheReadUsdPerMTok: 0.175,
186
+ },
187
+
188
+ // Google
189
+ "gemini-3.1-pro": {
190
+ inputUsdPerMTok: 2,
191
+ outputUsdPerMTok: 12,
192
+ cacheReadUsdPerMTok: 0.2,
193
+ },
194
+ "gemini-3-pro": {
195
+ inputUsdPerMTok: 2,
196
+ outputUsdPerMTok: 12,
197
+ cacheReadUsdPerMTok: 0.2,
198
+ },
199
+ "gemini-3.6-flash": {
200
+ inputUsdPerMTok: 1.5,
201
+ outputUsdPerMTok: 7.5,
202
+ cacheReadUsdPerMTok: 0.15,
203
+ },
204
+ "gemini-3.5-flash": {
205
+ inputUsdPerMTok: 1.5,
206
+ outputUsdPerMTok: 9,
207
+ cacheReadUsdPerMTok: 0.15,
208
+ },
209
+
210
+ // Cursor Composer
211
+ "composer-2.5": {
212
+ inputUsdPerMTok: 0.5,
213
+ outputUsdPerMTok: 2.5,
214
+ cacheReadUsdPerMTok: 0.2,
215
+ fast: {
216
+ inputUsdPerMTok: 3,
217
+ outputUsdPerMTok: 15,
218
+ cacheReadUsdPerMTok: 0.5,
219
+ },
220
+ },
221
+ "composer-2": {
222
+ inputUsdPerMTok: 0.5,
223
+ outputUsdPerMTok: 2.5,
224
+ cacheReadUsdPerMTok: 0.2,
225
+ },
226
+
227
+ // Moonshot / Zhipu
228
+ "kimi-k3": {
229
+ inputUsdPerMTok: 3,
230
+ outputUsdPerMTok: 15,
231
+ cacheReadUsdPerMTok: 0.3,
232
+ },
233
+ "kimi-k2.7": {
234
+ inputUsdPerMTok: 0.95,
235
+ outputUsdPerMTok: 4,
236
+ cacheReadUsdPerMTok: 0.19,
237
+ },
238
+ "kimi-k2.5": {
239
+ inputUsdPerMTok: 0.6,
240
+ outputUsdPerMTok: 3,
241
+ cacheReadUsdPerMTok: 0.1,
242
+ },
243
+ "glm-5.2": {
244
+ inputUsdPerMTok: 1.4,
245
+ outputUsdPerMTok: 4.4,
246
+ cacheReadUsdPerMTok: 0.26,
247
+ },
248
+ };
249
+
250
+ /** Family aliases → the model id they currently resolve to. */
251
+ const MODEL_ID_ALIASES: Readonly<Record<string, string>> = {
252
+ grok: "grok-4.20",
253
+ "grok-latest": "grok-4.3",
254
+ "grok-code-latest": "grok-code",
255
+ opus: "opus-5",
256
+ "opus-latest": "opus-5",
257
+ sonnet: "sonnet-4.6",
258
+ "sonnet-latest": "sonnet-5",
259
+ haiku: "haiku-4.5",
260
+ "haiku-latest": "haiku-4.5",
261
+ fable: "fable-5",
262
+ gpt: "gpt-5.6",
263
+ "gpt-latest": "gpt-5.6",
264
+ "gpt-5-6-sol": "gpt-5.6",
265
+ "gpt-5-5": "gpt-5.5",
266
+ "gpt-mini-latest": "gpt-mini",
267
+ codex: "codex-5.3",
268
+ "codex-latest": "codex-5.3",
269
+ gemini: "gemini-3.1-pro",
270
+ "gemini-latest": "gemini-3.1-pro",
271
+ "gemini-pro": "gemini-3.1-pro",
272
+ "gemini-pro-latest": "gemini-3.1-pro",
273
+ "gemini-flash": "gemini-3.6-flash",
274
+ "gemini-flash-latest": "gemini-3.6-flash",
275
+ composer: "composer-2.5",
276
+ "composer-latest": "composer-2.5",
277
+ "composer-2-5": "composer-2.5",
278
+ kimi: "kimi-k2.7",
279
+ "kimi-latest": "kimi-k2.7",
280
+ };
281
+
282
+ /** Env var holding a JSON pricing override merged over the defaults. */
283
+ export const MODEL_PRICING_ENV_VAR = "AGENT_SERVE_MODEL_PRICING";
284
+
285
+ let cachedEnvOverride:
286
+ | { raw: string | undefined; table: Record<string, ModelPricing> }
287
+ | undefined;
288
+
289
+ function envOverrideTable(): Record<string, ModelPricing> {
290
+ const raw = process.env[MODEL_PRICING_ENV_VAR];
291
+ if (cachedEnvOverride !== undefined && cachedEnvOverride.raw === raw) {
292
+ return cachedEnvOverride.table;
293
+ }
294
+ let table: Record<string, ModelPricing> = {};
295
+ if (raw !== undefined && raw.trim() !== "") {
296
+ try {
297
+ const parsed = JSON.parse(raw) as unknown;
298
+ if (typeof parsed === "object" && parsed !== null) {
299
+ for (const [key, value] of Object.entries(parsed)) {
300
+ const tier = parsePricing(value);
301
+ if (tier !== undefined) {
302
+ table[normalizeModelId(key)] = tier;
303
+ }
304
+ }
305
+ }
306
+ } catch {
307
+ table = {};
308
+ }
309
+ }
310
+ cachedEnvOverride = { raw, table };
311
+ return table;
312
+ }
313
+
314
+ function parseTier(value: unknown): ModelPricingTier | undefined {
315
+ if (typeof value !== "object" || value === null) {
316
+ return undefined;
317
+ }
318
+ const row = value as Record<string, unknown>;
319
+ const input = row.inputUsdPerMTok;
320
+ const output = row.outputUsdPerMTok;
321
+ const cacheRead = row.cacheReadUsdPerMTok;
322
+ if (
323
+ !isRate(input) ||
324
+ !isRate(output) ||
325
+ (cacheRead !== undefined && !isRate(cacheRead))
326
+ ) {
327
+ return undefined;
328
+ }
329
+ const cacheWrite = row.cacheWriteUsdPerMTok;
330
+ return {
331
+ inputUsdPerMTok: input,
332
+ outputUsdPerMTok: output,
333
+ cacheReadUsdPerMTok: isRate(cacheRead) ? cacheRead : input,
334
+ cacheWriteUsdPerMTok: isRate(cacheWrite) ? cacheWrite : undefined,
335
+ };
336
+ }
337
+
338
+ function parsePricing(value: unknown): ModelPricing | undefined {
339
+ const base = parseTier(value);
340
+ if (base === undefined) {
341
+ return undefined;
342
+ }
343
+ const fast = parseTier((value as Record<string, unknown>).fast);
344
+ return { ...base, fast };
345
+ }
346
+
347
+ function isRate(value: unknown): value is number {
348
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
349
+ }
350
+
351
+ export function normalizeModelId(id: string): string {
352
+ return id.trim().toLowerCase();
353
+ }
354
+
355
+ function modelId(model: ModelSetting): string {
356
+ return typeof model === "string" ? model : model.id;
357
+ }
358
+
359
+ function modelHasFastParam(model: ModelSetting): boolean {
360
+ if (typeof model === "string") {
361
+ return false;
362
+ }
363
+ return (
364
+ model.params?.some(
365
+ (param) => param.id === "fast" && param.value === "true"
366
+ ) === true
367
+ );
368
+ }
369
+
370
+ /**
371
+ * Rates for a model selection, honoring its `fast` parameter. `undefined`
372
+ * when the model is not in the table (no estimate is better than a wrong
373
+ * one).
374
+ */
375
+ export function resolveModelPricing(
376
+ model: ModelSetting
377
+ ): ModelPricingTier | undefined {
378
+ const overrides = envOverrideTable();
379
+ const rawId = normalizeModelId(modelId(model));
380
+ const canonicalId = MODEL_ID_ALIASES[rawId] ?? rawId;
381
+ const pricing =
382
+ overrides[rawId] ??
383
+ overrides[canonicalId] ??
384
+ DEFAULT_MODEL_PRICING[rawId] ??
385
+ DEFAULT_MODEL_PRICING[canonicalId];
386
+ if (pricing === undefined) {
387
+ return undefined;
388
+ }
389
+ if (modelHasFastParam(model) && pricing.fast !== undefined) {
390
+ return pricing.fast;
391
+ }
392
+ return pricing;
393
+ }
394
+
395
+ /**
396
+ * Estimate the dollar cost of one turn's token usage. `undefined` when the
397
+ * model has no known rates.
398
+ */
399
+ export function estimateTurnCost(
400
+ model: ModelSetting,
401
+ usage: TurnUsage
402
+ ): TurnCost | undefined {
403
+ const tier = resolveModelPricing(model);
404
+ if (tier === undefined) {
405
+ return undefined;
406
+ }
407
+ const cacheWriteRate = tier.cacheWriteUsdPerMTok ?? tier.inputUsdPerMTok;
408
+ const inputUsd = (usage.inputTokens / M) * tier.inputUsdPerMTok;
409
+ const outputUsd = (usage.outputTokens / M) * tier.outputUsdPerMTok;
410
+ const cacheReadUsd = (usage.cacheReadTokens / M) * tier.cacheReadUsdPerMTok;
411
+ const cacheWriteUsd = (usage.cacheWriteTokens / M) * cacheWriteRate;
412
+ return {
413
+ totalUsd: roundUsd(inputUsd + outputUsd + cacheReadUsd + cacheWriteUsd),
414
+ inputUsd: roundUsd(inputUsd),
415
+ outputUsd: roundUsd(outputUsd),
416
+ cacheReadUsd: roundUsd(cacheReadUsd),
417
+ cacheWriteUsd: roundUsd(cacheWriteUsd),
418
+ model: normalizeModelId(modelId(model)),
419
+ source: "estimate",
420
+ };
421
+ }
422
+
423
+ /** Keep persisted estimates readable: micro-dollar precision. */
424
+ function roundUsd(usd: number): number {
425
+ return Math.round(usd * 1e6) / 1e6;
426
+ }
@@ -431,9 +431,17 @@ export class CursorSdkRunner implements AgentRunner {
431
431
  if (request.isFirstTurn) {
432
432
  return Agent.create(options);
433
433
  }
434
- throw new Error(
435
- `Cloud session ${request.sessionId} is missing sdkAgentId; cannot resume.`
434
+ // A cloud session past its first turn should carry the bound agent id
435
+ // (the engine persists it via onSdkAgentId as soon as create() reports
436
+ // one). Reaching here means the binding turn failed before the server
437
+ // ever minted an agent — e.g. create() rejected its PR attach — so the
438
+ // session would otherwise fail every future turn. Mirror the local
439
+ // host-replacement path above: a fresh conversation beats failing this
440
+ // thread forever, and the new id binds to the session for later resumes.
441
+ this.log(
442
+ `[agentkit] cloud session ${request.sessionId} has no bound cloud agent; creating a fresh one`
436
443
  );
444
+ return Agent.create(options);
437
445
  }
438
446
 
439
447
  private getStore(stateDir: string): Promise<LocalAgentStore> {