@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.
- package/dist/ab.d.ts +6 -0
- package/dist/ab.d.ts.map +1 -1
- package/dist/ab.js +1 -0
- package/dist/artifacts.d.ts +34 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +57 -0
- package/dist/bin/agent-serve.js +31 -9
- package/dist/channels/github/github-channel.d.ts +1 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +14 -1
- package/dist/channels/github/types.d.ts +6 -1
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/slack/api.d.ts +2 -1
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +1 -0
- package/dist/channels/slack/cursor-account.d.ts +2 -1
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +1 -1
- package/dist/channels/slack/dispatch.d.ts +4 -1
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +3 -3
- package/dist/channels/slack/interactive.d.ts +2 -1
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +1 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +4 -3
- package/dist/channels/slack/types.d.ts +6 -1
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +1 -1
- package/dist/docs/ab.html +2 -2
- package/dist/docs/assets/{app.DYcC9FY-.js → app.BICLoJYF.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.CQJytDwU.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.o4N_knTV.js → VPLocalSearchBox.wN4Ko9dT.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DQ-njyo0.js → theme.dnnTE-Co.js} +2 -2
- package/dist/docs/building-with-agents.html +2 -2
- package/dist/docs/concepts.html +2 -2
- package/dist/docs/deployment.html +2 -2
- package/dist/docs/evals.html +2 -2
- package/dist/docs/example-agents/approval-buddy.html +2 -2
- package/dist/docs/example-agents/benny.html +2 -2
- package/dist/docs/example-agents/bugbot.html +2 -2
- package/dist/docs/example-agents/codebase-wiki.html +2 -2
- package/dist/docs/example-agents/codeowners-review.html +2 -2
- package/dist/docs/example-agents/concierge.html +2 -2
- package/dist/docs/example-agents/fsd.html +2 -2
- package/dist/docs/example-agents/index.html +2 -2
- package/dist/docs/example-agents/knowledge-base.html +2 -2
- package/dist/docs/example-agents/oncall.html +2 -2
- package/dist/docs/example-agents/security-reviewer.html +2 -2
- package/dist/docs/example-agents/slack-agent.html +2 -2
- package/dist/docs/example-agents/weather-agent.html +2 -2
- package/dist/docs/guides/agent-to-agent.html +2 -2
- package/dist/docs/guides/cloud-runtime.html +2 -2
- package/dist/docs/guides/github.html +2 -2
- package/dist/docs/guides/human-in-the-loop.html +2 -2
- package/dist/docs/guides/mcp-oauth.html +2 -2
- package/dist/docs/guides/slack.html +2 -2
- package/dist/docs/guides/webhooks.html +2 -2
- package/dist/docs/hillclimbing.html +2 -2
- package/dist/docs/index.html +2 -2
- package/dist/docs/quickstart.html +2 -2
- package/dist/docs/reference/agent-config.html +2 -2
- package/dist/docs/reference/channels.html +2 -2
- package/dist/docs/reference/cli.html +2 -2
- package/dist/docs/reference/connections.html +2 -2
- package/dist/docs/reference/hooks.html +2 -2
- package/dist/docs/reference/http-api.html +2 -2
- package/dist/docs/reference/instructions.html +2 -2
- package/dist/docs/reference/playground.html +2 -2
- package/dist/docs/reference/project-layout.html +2 -2
- package/dist/docs/reference/prompt.html +2 -2
- package/dist/docs/reference/schedules.html +2 -2
- package/dist/docs/reference/sessions.html +2 -2
- package/dist/docs/reference/skills.html +2 -2
- package/dist/docs/reference/subagents.html +2 -2
- package/dist/docs/reference/tools.html +2 -2
- package/dist/docs/scaffolding-agents.html +2 -2
- package/dist/docs/storage.html +2 -2
- package/dist/docs/troubleshooting.html +2 -2
- package/dist/evals/assertions.d.ts +10 -1
- package/dist/evals/assertions.d.ts.map +1 -1
- package/dist/evals/assertions.js +12 -0
- package/dist/evals.d.ts +1 -1
- package/dist/evals.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/ab-fold.d.ts.map +1 -1
- package/dist/internal/ab-fold.js +4 -0
- package/dist/internal/ab-snapshot.d.ts.map +1 -1
- package/dist/internal/ab-snapshot.js +1 -0
- package/dist/internal/artifacts-store.d.ts +45 -0
- package/dist/internal/artifacts-store.d.ts.map +1 -0
- package/dist/internal/artifacts-store.js +250 -0
- package/dist/internal/builtin-tools/artifacts.d.ts +10 -0
- package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -0
- package/dist/internal/builtin-tools/artifacts.js +73 -0
- package/dist/internal/builtin-tools/index.d.ts +8 -1
- package/dist/internal/builtin-tools/index.d.ts.map +1 -1
- package/dist/internal/builtin-tools/index.js +15 -0
- package/dist/internal/cli-ax.d.ts +11 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +80 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +14 -6
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +83 -2
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/model-pricing.d.ts +49 -0
- package/dist/internal/model-pricing.d.ts.map +1 -0
- package/dist/internal/model-pricing.js +377 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +1 -0
- package/dist/internal/schedule-runner.d.ts.map +1 -1
- package/dist/internal/schedule-runner.js +1 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +9 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +39 -0
- package/dist/internal/session-cost.d.ts +49 -0
- package/dist/internal/session-cost.d.ts.map +1 -0
- package/dist/internal/session-cost.js +175 -0
- package/dist/internal/session-engine.d.ts +35 -1
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +128 -7
- package/dist/internal/storage-coordinator.d.ts +15 -0
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +52 -0
- package/dist/internal/trajectory.d.ts +9 -1
- package/dist/internal/trajectory.d.ts.map +1 -1
- package/dist/internal/trajectory.js +32 -5
- package/dist/playground/assets/index-BDCeW4uc.js +86 -0
- package/dist/playground/assets/index-Bul1WfrB.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage.d.ts +4 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +4 -0
- package/dist/types.d.ts +164 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +26 -26
- package/src/ab.ts +7 -0
- package/src/bin/agent-serve.ts +30 -2
- package/src/internal/ab-fold.ts +4 -0
- package/src/internal/ab-snapshot.ts +1 -0
- package/src/internal/cli-ax.ts +95 -0
- package/src/internal/model-pricing.ts +426 -0
- package/src/internal/sdk-runner.ts +10 -2
- package/src/internal/session-cost.ts +231 -0
- package/src/internal/session-engine.ts +28 -0
- package/src/internal/trajectory.ts +45 -3
- package/src/types.ts +24 -1
- package/dist/channels/github/instrument.d.ts +0 -20
- package/dist/channels/github/instrument.d.ts.map +0 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BQTzJjR_.js +0 -1
- package/dist/internal/json-dir-store.d.ts +0 -32
- package/dist/internal/json-dir-store.d.ts.map +0 -1
- package/dist/internal/json-dir-store.js +0 -100
- package/dist/playground/assets/index-CjOQ4hN9.css +0 -1
- package/dist/playground/assets/index-DqXdAFGa.js +0 -85
- package/src/bin/agent-serve.version.test.ts +0 -64
- package/src/channels/github/api.test.ts +0 -64
- package/src/channels/github/auth.test.ts +0 -105
- package/src/channels/github/cursor-account.test.ts +0 -204
- package/src/channels/github/forward.test.ts +0 -457
- package/src/channels/github/github.test.ts +0 -985
- package/src/channels/github/replay.test.ts +0 -179
- package/src/channels/slack/api.post-message.test.ts +0 -148
- package/src/channels/slack/approvals.test.ts +0 -328
- package/src/channels/slack/block-actions.test.ts +0 -469
- package/src/channels/slack/bot-mentions.test.ts +0 -267
- package/src/channels/slack/channel-watch.test.ts +0 -363
- package/src/channels/slack/cursor-account.test.ts +0 -301
- package/src/channels/slack/defaults.final-post.test.ts +0 -182
- package/src/channels/slack/dispatch.test.ts +0 -875
- package/src/channels/slack/eval-directive.test.ts +0 -273
- package/src/channels/slack/message-body.test.ts +0 -54
- package/src/channels/slack/nudge-store.test.ts +0 -143
- package/src/channels/slack/slack.test.ts +0 -391
- package/src/channels/slack/stop.test.ts +0 -23
- package/src/channels/slack/thread-context.test.ts +0 -202
- package/src/evals/assertions.test.ts +0 -617
- package/src/evals/expect.test.ts +0 -144
- package/src/evals/judge.test.ts +0 -181
- package/src/evals/loaders.test.ts +0 -132
- package/src/evals/matchers.test.ts +0 -95
- package/src/evals/reporters.test.ts +0 -303
- package/src/evals/run-facts.test.ts +0 -259
- package/src/internal/ab-snapshot.test.ts +0 -325
- package/src/internal/approval-gate.test.ts +0 -49
- package/src/internal/approvals.integration.test.ts +0 -383
- package/src/internal/artifacts-store.test.ts +0 -283
- package/src/internal/authored-loaders.test.ts +0 -31
- package/src/internal/builtin-tools/artifacts.test.ts +0 -247
- package/src/internal/builtin-tools/reminders.test.ts +0 -201
- package/src/internal/channel-route-schema.test.ts +0 -294
- package/src/internal/chat-attach.test.ts +0 -262
- package/src/internal/cli-deploy.test.ts +0 -1991
- package/src/internal/cli-docs.test.ts +0 -161
- package/src/internal/cli-mcp.test.ts +0 -789
- package/src/internal/cli-skills.test.ts +0 -133
- package/src/internal/cli-slack.test.ts +0 -1654
- package/src/internal/cloud-merge.test.ts +0 -74
- package/src/internal/cron.test.ts +0 -22
- package/src/internal/cursor/account-mcp.test.ts +0 -807
- package/src/internal/cursor/backend-client.test.ts +0 -591
- package/src/internal/cursor/credentials.test.ts +0 -351
- package/src/internal/cursor/github-credentials.test.ts +0 -136
- package/src/internal/cursor-account-mcp-auth.test.ts +0 -310
- package/src/internal/cursor-account.integration.test.ts +0 -441
- package/src/internal/cursor-event-relay.test.ts +0 -746
- package/src/internal/cursor-github-credentials.integration.test.ts +0 -271
- package/src/internal/cursor-slack-relay.test.ts +0 -525
- package/src/internal/deploy-source.test.ts +0 -111
- package/src/internal/discovery.artifact-tool.test.ts +0 -136
- package/src/internal/discovery.artifacts.test.ts +0 -119
- package/src/internal/discovery.builtin-tools.test.ts +0 -94
- package/src/internal/discovery.concurrency.test.ts +0 -60
- package/src/internal/discovery.cursor-account.test.ts +0 -133
- package/src/internal/discovery.cwd.test.ts +0 -83
- package/src/internal/discovery.hosting.test.ts +0 -80
- package/src/internal/discovery.identity.test.ts +0 -44
- package/src/internal/docs-site.test.ts +0 -66
- package/src/internal/duration.test.ts +0 -29
- package/src/internal/eval-judge-model.test.ts +0 -187
- package/src/internal/eval-run-store.cancel.test.ts +0 -142
- package/src/internal/eval-run-store.storage.test.ts +0 -211
- package/src/internal/eval-runner.http.test.ts +0 -403
- package/src/internal/eval-runner.run.test.ts +0 -928
- package/src/internal/evals-client.test.ts +0 -307
- package/src/internal/event-mapper.test.ts +0 -243
- package/src/internal/github-fanout.test.ts +0 -213
- package/src/internal/handleAgentServeTrigger.test.ts +0 -185
- package/src/internal/host-kv.test.ts +0 -82
- package/src/internal/host-platforms.test.ts +0 -126
- package/src/internal/http-channel.test.ts +0 -402
- package/src/internal/init-project.test.ts +0 -270
- package/src/internal/install-cursor-skills.test.ts +0 -262
- package/src/internal/local-env.test.ts +0 -120
- package/src/internal/log-ring.test.ts +0 -31
- package/src/internal/logs-client.test.ts +0 -350
- package/src/internal/mcp-endpoint.test.ts +0 -436
- package/src/internal/mcp-host.test.ts +0 -298
- package/src/internal/mcp-oauth.test.ts +0 -148
- package/src/internal/net.test.ts +0 -17
- package/src/internal/peer-connections.test.ts +0 -128
- package/src/internal/peer-mcp.integration.test.ts +0 -289
- package/src/internal/playground/toolchain.test.ts +0 -53
- package/src/internal/playground-cli.test.ts +0 -187
- package/src/internal/playground-proxy.test.ts +0 -376
- package/src/internal/prompt-context.integration.test.ts +0 -232
- package/src/internal/prompt-context.test.ts +0 -127
- package/src/internal/reminder-runner.test.ts +0 -432
- package/src/internal/reminder-store.test.ts +0 -53
- package/src/internal/request-headers.test.ts +0 -27
- package/src/internal/resolve-prod-target.test.ts +0 -787
- package/src/internal/resolved-connections.test.ts +0 -295
- package/src/internal/router.test.ts +0 -57
- package/src/internal/sdk-runner.test.ts +0 -290
- package/src/internal/server.artifacts.test.ts +0 -334
- package/src/internal/session-engine.artifacts.test.ts +0 -293
- package/src/internal/session-engine.coalesce.test.ts +0 -169
- package/src/internal/session-engine.concurrency.test.ts +0 -250
- package/src/internal/session-engine.host-oauth-mcp.test.ts +0 -110
- package/src/internal/session-engine.interrupt.test.ts +0 -577
- package/src/internal/session-engine.storage.test.ts +0 -547
- package/src/internal/session-urls.test.ts +0 -28
- package/src/internal/sessions-client.test.ts +0 -518
- package/src/internal/storage-coordinator.test.ts +0 -517
- package/src/internal/tool-call.test.ts +0 -458
- package/src/internal/tool-result.test.ts +0 -52
- package/src/internal/trajectory.approvals.test.ts +0 -83
- package/src/internal/trajectory.subagents.test.ts +0 -198
- package/src/internal/turn-governor.test.ts +0 -137
- package/src/internal/update-check.test.ts +0 -485
- package/src/internal/workspace.test.ts +0 -207
- package/src/storage-backends/cursor-hosted.test.ts +0 -121
package/src/bin/agent-serve.ts
CHANGED
|
@@ -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);
|
package/src/internal/ab-fold.ts
CHANGED
|
@@ -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;
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -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
|
-
|
|
435
|
-
|
|
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> {
|