@cursor/july 0.1.25 → 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 (288) 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/channels/github/github-channel.ts +19 -2
  145. package/src/channels/github/types.ts +6 -0
  146. package/src/channels/slack/api.ts +3 -1
  147. package/src/channels/slack/cursor-account.ts +3 -1
  148. package/src/channels/slack/dispatch.ts +12 -1
  149. package/src/channels/slack/interactive.ts +3 -0
  150. package/src/channels/slack/slack-channel.ts +6 -0
  151. package/src/channels/slack/types.ts +6 -0
  152. package/src/internal/ab-fold.ts +4 -0
  153. package/src/internal/ab-snapshot.ts +1 -0
  154. package/src/internal/cli-ax.ts +95 -0
  155. package/src/internal/model-pricing.ts +426 -0
  156. package/src/internal/reminder-runner.ts +1 -0
  157. package/src/internal/schedule-runner.ts +1 -0
  158. package/src/internal/sdk-runner.ts +10 -2
  159. package/src/internal/server.ts +2 -0
  160. package/src/internal/session-cost.ts +231 -0
  161. package/src/internal/session-engine.ts +32 -1
  162. package/src/internal/trajectory.ts +45 -3
  163. package/src/types.ts +42 -2
  164. package/dist/channels/github/instrument.d.ts +0 -20
  165. package/dist/channels/github/instrument.d.ts.map +0 -1
  166. package/dist/docs/assets/chunks/@localSearchIndexroot.BQTzJjR_.js +0 -1
  167. package/dist/internal/json-dir-store.d.ts +0 -32
  168. package/dist/internal/json-dir-store.d.ts.map +0 -1
  169. package/dist/internal/json-dir-store.js +0 -100
  170. package/dist/playground/assets/index-CjOQ4hN9.css +0 -1
  171. package/dist/playground/assets/index-DqXdAFGa.js +0 -85
  172. package/src/bin/agent-serve.version.test.ts +0 -64
  173. package/src/channels/github/api.test.ts +0 -64
  174. package/src/channels/github/auth.test.ts +0 -105
  175. package/src/channels/github/cursor-account.test.ts +0 -204
  176. package/src/channels/github/forward.test.ts +0 -457
  177. package/src/channels/github/github.test.ts +0 -943
  178. package/src/channels/github/replay.test.ts +0 -179
  179. package/src/channels/slack/api.post-message.test.ts +0 -148
  180. package/src/channels/slack/approvals.test.ts +0 -328
  181. package/src/channels/slack/block-actions.test.ts +0 -452
  182. package/src/channels/slack/bot-mentions.test.ts +0 -267
  183. package/src/channels/slack/channel-watch.test.ts +0 -363
  184. package/src/channels/slack/cursor-account.test.ts +0 -253
  185. package/src/channels/slack/defaults.final-post.test.ts +0 -182
  186. package/src/channels/slack/dispatch.test.ts +0 -795
  187. package/src/channels/slack/eval-directive.test.ts +0 -273
  188. package/src/channels/slack/message-body.test.ts +0 -54
  189. package/src/channels/slack/nudge-store.test.ts +0 -143
  190. package/src/channels/slack/slack.test.ts +0 -391
  191. package/src/channels/slack/stop.test.ts +0 -23
  192. package/src/channels/slack/thread-context.test.ts +0 -202
  193. package/src/evals/assertions.test.ts +0 -617
  194. package/src/evals/expect.test.ts +0 -144
  195. package/src/evals/judge.test.ts +0 -181
  196. package/src/evals/loaders.test.ts +0 -132
  197. package/src/evals/matchers.test.ts +0 -95
  198. package/src/evals/reporters.test.ts +0 -303
  199. package/src/evals/run-facts.test.ts +0 -259
  200. package/src/internal/ab-snapshot.test.ts +0 -325
  201. package/src/internal/approval-gate.test.ts +0 -49
  202. package/src/internal/approvals.integration.test.ts +0 -383
  203. package/src/internal/artifacts-store.test.ts +0 -283
  204. package/src/internal/authored-loaders.test.ts +0 -31
  205. package/src/internal/builtin-tools/artifacts.test.ts +0 -247
  206. package/src/internal/builtin-tools/reminders.test.ts +0 -201
  207. package/src/internal/channel-route-schema.test.ts +0 -294
  208. package/src/internal/chat-attach.test.ts +0 -262
  209. package/src/internal/cli-deploy.test.ts +0 -1991
  210. package/src/internal/cli-docs.test.ts +0 -161
  211. package/src/internal/cli-mcp.test.ts +0 -789
  212. package/src/internal/cli-skills.test.ts +0 -133
  213. package/src/internal/cli-slack.test.ts +0 -1654
  214. package/src/internal/cloud-merge.test.ts +0 -74
  215. package/src/internal/cron.test.ts +0 -22
  216. package/src/internal/cursor/account-mcp.test.ts +0 -807
  217. package/src/internal/cursor/backend-client.test.ts +0 -591
  218. package/src/internal/cursor/credentials.test.ts +0 -351
  219. package/src/internal/cursor/github-credentials.test.ts +0 -136
  220. package/src/internal/cursor-account-mcp-auth.test.ts +0 -310
  221. package/src/internal/cursor-account.integration.test.ts +0 -441
  222. package/src/internal/cursor-event-relay.test.ts +0 -746
  223. package/src/internal/cursor-github-credentials.integration.test.ts +0 -271
  224. package/src/internal/cursor-slack-relay.test.ts +0 -525
  225. package/src/internal/deploy-source.test.ts +0 -111
  226. package/src/internal/discovery.artifact-tool.test.ts +0 -136
  227. package/src/internal/discovery.artifacts.test.ts +0 -119
  228. package/src/internal/discovery.builtin-tools.test.ts +0 -94
  229. package/src/internal/discovery.concurrency.test.ts +0 -60
  230. package/src/internal/discovery.cursor-account.test.ts +0 -133
  231. package/src/internal/discovery.cwd.test.ts +0 -83
  232. package/src/internal/discovery.hosting.test.ts +0 -80
  233. package/src/internal/discovery.identity.test.ts +0 -44
  234. package/src/internal/docs-site.test.ts +0 -66
  235. package/src/internal/duration.test.ts +0 -29
  236. package/src/internal/eval-judge-model.test.ts +0 -187
  237. package/src/internal/eval-run-store.cancel.test.ts +0 -142
  238. package/src/internal/eval-run-store.storage.test.ts +0 -211
  239. package/src/internal/eval-runner.http.test.ts +0 -403
  240. package/src/internal/eval-runner.run.test.ts +0 -928
  241. package/src/internal/evals-client.test.ts +0 -307
  242. package/src/internal/event-mapper.test.ts +0 -243
  243. package/src/internal/github-fanout.test.ts +0 -213
  244. package/src/internal/handleAgentServeTrigger.test.ts +0 -185
  245. package/src/internal/host-kv.test.ts +0 -82
  246. package/src/internal/host-platforms.test.ts +0 -126
  247. package/src/internal/http-channel.test.ts +0 -402
  248. package/src/internal/init-project.test.ts +0 -270
  249. package/src/internal/install-cursor-skills.test.ts +0 -262
  250. package/src/internal/local-env.test.ts +0 -120
  251. package/src/internal/log-ring.test.ts +0 -31
  252. package/src/internal/logs-client.test.ts +0 -350
  253. package/src/internal/mcp-endpoint.test.ts +0 -436
  254. package/src/internal/mcp-host.test.ts +0 -298
  255. package/src/internal/mcp-oauth.test.ts +0 -148
  256. package/src/internal/net.test.ts +0 -17
  257. package/src/internal/peer-connections.test.ts +0 -128
  258. package/src/internal/peer-mcp.integration.test.ts +0 -289
  259. package/src/internal/playground/toolchain.test.ts +0 -53
  260. package/src/internal/playground-cli.test.ts +0 -187
  261. package/src/internal/playground-proxy.test.ts +0 -376
  262. package/src/internal/prompt-context.integration.test.ts +0 -232
  263. package/src/internal/prompt-context.test.ts +0 -127
  264. package/src/internal/reminder-runner.test.ts +0 -390
  265. package/src/internal/reminder-store.test.ts +0 -53
  266. package/src/internal/request-headers.test.ts +0 -27
  267. package/src/internal/resolve-prod-target.test.ts +0 -787
  268. package/src/internal/resolved-connections.test.ts +0 -295
  269. package/src/internal/router.test.ts +0 -57
  270. package/src/internal/sdk-runner.test.ts +0 -290
  271. package/src/internal/server.artifacts.test.ts +0 -303
  272. package/src/internal/session-engine.artifacts.test.ts +0 -243
  273. package/src/internal/session-engine.coalesce.test.ts +0 -169
  274. package/src/internal/session-engine.concurrency.test.ts +0 -250
  275. package/src/internal/session-engine.host-oauth-mcp.test.ts +0 -110
  276. package/src/internal/session-engine.interrupt.test.ts +0 -577
  277. package/src/internal/session-engine.storage.test.ts +0 -547
  278. package/src/internal/session-urls.test.ts +0 -28
  279. package/src/internal/sessions-client.test.ts +0 -518
  280. package/src/internal/storage-coordinator.test.ts +0 -517
  281. package/src/internal/tool-call.test.ts +0 -458
  282. package/src/internal/tool-result.test.ts +0 -52
  283. package/src/internal/trajectory.approvals.test.ts +0 -83
  284. package/src/internal/trajectory.subagents.test.ts +0 -198
  285. package/src/internal/turn-governor.test.ts +0 -137
  286. package/src/internal/update-check.test.ts +0 -485
  287. package/src/internal/workspace.test.ts +0 -207
  288. package/src/storage-backends/cursor-hosted.test.ts +0 -121
@@ -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
+ }
@@ -664,6 +664,7 @@ export class ReminderRunner {
664
664
  host: this.engine.host,
665
665
  mcp: this.engine.host.mcp,
666
666
  appAuth: APP_AUTH,
667
+ artifacts: this.engine.artifacts,
667
668
  async followup(args: { message: string }) {
668
669
  const auth = await self.resolveFollowupAuth(
669
670
  record.channelId,
@@ -151,6 +151,7 @@ export class ScheduleRunner {
151
151
  },
152
152
  appAuth: APP_AUTH,
153
153
  host: this.engine.host,
154
+ artifacts: this.engine.artifacts,
154
155
  });
155
156
  // Collect ids from receive() calls the handler chose not to await
156
157
  // (commonly wrapped in waitUntil); receive resolves at message accept,
@@ -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> {
@@ -494,6 +494,7 @@ export async function startServer(
494
494
  interruptContinuation: (continuationToken) =>
495
495
  engine.interruptContinuation(channel.id, continuationToken),
496
496
  logger,
497
+ artifacts: engine.artifacts,
497
498
  };
498
499
  try {
499
500
  await definition.onStart(startArgs);
@@ -1778,6 +1779,7 @@ async function startCursorSlackRelay(input: {
1778
1779
  envelope,
1779
1780
  send: args.send,
1780
1781
  host: args.host,
1782
+ artifacts: args.artifacts,
1781
1783
  hasContinuationSession: (continuationToken) =>
1782
1784
  target.runtime.engine.hasContinuationSession(
1783
1785
  target.channelId,
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Per-session cost metrics folded from a durable session event stream.
3
+ *
4
+ * Powers `agent-serve cost [sessionId]`: each `turn.completed` event carries
5
+ * the turn's token usage and the estimated dollar cost recorded by the
6
+ * engine (see `model-pricing.ts`), so a session's spend is a pure fold over
7
+ * its `events.ndjson` — no server-side aggregation required.
8
+ */
9
+
10
+ import type { SessionEvent, TurnCost, TurnUsage } from "../types.js";
11
+ import { formatCostUsd, roundCostUsd } from "./trajectory.js";
12
+
13
+ /** One agent loop pass (turn) with its usage and estimated cost. */
14
+ export interface SessionCostTurn {
15
+ turnId: string;
16
+ failed: boolean;
17
+ usage?: TurnUsage;
18
+ cost?: TurnCost;
19
+ }
20
+
21
+ export interface SessionCostTotals {
22
+ turns: number;
23
+ turnFailures: number;
24
+ inputTokens: number;
25
+ outputTokens: number;
26
+ cacheReadTokens: number;
27
+ cacheWriteTokens: number;
28
+ /** Sum of per-turn estimated costs (USD). */
29
+ costUsd: number;
30
+ /** Turns that reported usage but had no known model rates. */
31
+ unpricedTurns: number;
32
+ }
33
+
34
+ export interface SessionCostReport {
35
+ sessionId: string;
36
+ turns: SessionCostTurn[];
37
+ totals: SessionCostTotals;
38
+ }
39
+
40
+ export function emptySessionCostTotals(): SessionCostTotals {
41
+ return {
42
+ turns: 0,
43
+ turnFailures: 0,
44
+ inputTokens: 0,
45
+ outputTokens: 0,
46
+ cacheReadTokens: 0,
47
+ cacheWriteTokens: 0,
48
+ costUsd: 0,
49
+ unpricedTurns: 0,
50
+ };
51
+ }
52
+
53
+ export function addSessionCostTotals(
54
+ target: SessionCostTotals,
55
+ delta: SessionCostTotals
56
+ ): void {
57
+ target.turns += delta.turns;
58
+ target.turnFailures += delta.turnFailures;
59
+ target.inputTokens += delta.inputTokens;
60
+ target.outputTokens += delta.outputTokens;
61
+ target.cacheReadTokens += delta.cacheReadTokens;
62
+ target.cacheWriteTokens += delta.cacheWriteTokens;
63
+ target.costUsd = roundCostUsd(target.costUsd + delta.costUsd);
64
+ target.unpricedTurns += delta.unpricedTurns;
65
+ }
66
+
67
+ /** Fold a session event stream into per-turn and total cost metrics. */
68
+ export function buildSessionCostReport(
69
+ events: readonly SessionEvent[]
70
+ ): SessionCostReport {
71
+ let sessionId = "";
72
+ const turns: SessionCostTurn[] = [];
73
+ const totals = emptySessionCostTotals();
74
+
75
+ for (const event of events) {
76
+ if (sessionId === "" && typeof event.sessionId === "string") {
77
+ sessionId = event.sessionId;
78
+ }
79
+ if (event.type !== "turn.completed" && event.type !== "turn.failed") {
80
+ continue;
81
+ }
82
+ const turn: SessionCostTurn = {
83
+ turnId: event.turnId ?? `turn_${turns.length + 1}`,
84
+ failed: event.type === "turn.failed",
85
+ };
86
+ totals.turns += 1;
87
+ if (event.type === "turn.failed") {
88
+ totals.turnFailures += 1;
89
+ turns.push(turn);
90
+ continue;
91
+ }
92
+ const usage = event.data.usage;
93
+ if (usage !== undefined) {
94
+ turn.usage = usage;
95
+ totals.inputTokens += usage.inputTokens;
96
+ totals.outputTokens += usage.outputTokens;
97
+ totals.cacheReadTokens += usage.cacheReadTokens;
98
+ totals.cacheWriteTokens += usage.cacheWriteTokens;
99
+ }
100
+ const cost = event.data.cost;
101
+ if (cost !== undefined) {
102
+ turn.cost = cost;
103
+ totals.costUsd = roundCostUsd(totals.costUsd + cost.totalUsd);
104
+ } else if (usage !== undefined) {
105
+ totals.unpricedTurns += 1;
106
+ }
107
+ turns.push(turn);
108
+ }
109
+
110
+ return { sessionId, turns, totals };
111
+ }
112
+
113
+ function formatCount(value: number): string {
114
+ return String(value);
115
+ }
116
+
117
+ /** Per-turn table for `agent-serve cost <sessionId>`. */
118
+ export function formatSessionCostText(report: SessionCostReport): string {
119
+ const lines: string[] = [];
120
+ lines.push(
121
+ `session ${report.sessionId || "(unknown)"} est. cost: ${formatCostUsd(report.totals.costUsd)}`
122
+ );
123
+ if (report.turns.length === 0) {
124
+ lines.push("No turns recorded.");
125
+ return `${lines.join("\n")}\n`;
126
+ }
127
+ const header = [
128
+ "TURN",
129
+ "IN",
130
+ "OUT",
131
+ "CACHE R",
132
+ "CACHE W",
133
+ "EST COST",
134
+ "MODEL",
135
+ "STATUS",
136
+ ];
137
+ const rows = report.turns.map((turn) => [
138
+ turn.turnId,
139
+ turn.usage === undefined ? "—" : formatCount(turn.usage.inputTokens),
140
+ turn.usage === undefined ? "—" : formatCount(turn.usage.outputTokens),
141
+ turn.usage === undefined ? "—" : formatCount(turn.usage.cacheReadTokens),
142
+ turn.usage === undefined ? "—" : formatCount(turn.usage.cacheWriteTokens),
143
+ turn.cost === undefined ? "—" : formatCostUsd(turn.cost.totalUsd),
144
+ turn.cost?.model ?? "—",
145
+ turn.failed ? "failed" : "ok",
146
+ ]);
147
+ rows.push([
148
+ "total",
149
+ formatCount(report.totals.inputTokens),
150
+ formatCount(report.totals.outputTokens),
151
+ formatCount(report.totals.cacheReadTokens),
152
+ formatCount(report.totals.cacheWriteTokens),
153
+ formatCostUsd(report.totals.costUsd),
154
+ "",
155
+ report.totals.turnFailures > 0
156
+ ? `${report.totals.turnFailures} failed`
157
+ : "",
158
+ ]);
159
+ lines.push(renderTable(header, rows));
160
+ appendUnpricedNote(lines, report.totals.unpricedTurns);
161
+ return `${lines.join("\n")}\n`;
162
+ }
163
+
164
+ export interface SessionCostRow {
165
+ sessionId: string;
166
+ channelId: string;
167
+ title?: string;
168
+ updatedAt?: string;
169
+ totals: SessionCostTotals;
170
+ }
171
+
172
+ /** All-sessions table for `agent-serve cost` (no session id). */
173
+ export function formatSessionsCostText(rows: SessionCostRow[]): string {
174
+ if (rows.length === 0) {
175
+ return "No sessions.\n";
176
+ }
177
+ const header = [
178
+ "SESSION",
179
+ "CHANNEL",
180
+ "TURNS",
181
+ "IN",
182
+ "OUT",
183
+ "EST COST",
184
+ "UPDATED",
185
+ ];
186
+ const totals = emptySessionCostTotals();
187
+ const body = rows.map((row) => {
188
+ addSessionCostTotals(totals, row.totals);
189
+ return [
190
+ row.sessionId,
191
+ row.channelId,
192
+ formatCount(row.totals.turns),
193
+ formatCount(row.totals.inputTokens),
194
+ formatCount(row.totals.outputTokens),
195
+ formatCostUsd(row.totals.costUsd),
196
+ row.updatedAt ?? "",
197
+ ];
198
+ });
199
+ body.push([
200
+ "total",
201
+ "",
202
+ formatCount(totals.turns),
203
+ formatCount(totals.inputTokens),
204
+ formatCount(totals.outputTokens),
205
+ formatCostUsd(totals.costUsd),
206
+ "",
207
+ ]);
208
+ const lines = [renderTable(header, body)];
209
+ appendUnpricedNote(lines, totals.unpricedTurns);
210
+ return `${lines.join("\n")}\n`;
211
+ }
212
+
213
+ function appendUnpricedNote(lines: string[], unpricedTurns: number): void {
214
+ if (unpricedTurns > 0) {
215
+ lines.push(
216
+ `note: ${unpricedTurns} turn(s) reported usage for a model with no known rates; est. cost excludes them.`
217
+ );
218
+ }
219
+ }
220
+
221
+ function renderTable(header: string[], rows: string[][]): string {
222
+ const widths = header.map((h, i) =>
223
+ Math.max(h.length, ...rows.map((row) => (row[i] ?? "").length))
224
+ );
225
+ const renderRow = (row: string[]): string =>
226
+ row
227
+ .map((cell, i) => cell.padEnd(widths[i] ?? 0))
228
+ .join(" ")
229
+ .trimEnd();
230
+ return [renderRow(header), ...rows.map(renderRow)].join("\n");
231
+ }