@cursor/july 0.1.26 → 0.1.28
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 +43 -12
- 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/auth.d.ts +13 -13
- package/dist/channels/slack/auth.d.ts.map +1 -1
- package/dist/channels/slack/auth.js +19 -10
- 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 +3 -1
- package/dist/channels/slack/defaults.d.ts +7 -4
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +14 -4
- package/dist/channels/slack/dispatch.d.ts +10 -1
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +59 -21
- package/dist/channels/slack/external-policy.d.ts +27 -0
- package/dist/channels/slack/external-policy.d.ts.map +1 -0
- package/dist/channels/slack/external-policy.js +53 -0
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/interactive.d.ts +8 -1
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +43 -15
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +22 -8
- package/dist/channels/slack/types.d.ts +16 -1
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.DYcC9FY-.js → app.CqjrYaT2.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.VWQoPFJ1.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.o4N_knTV.js → VPLocalSearchBox.ag-MwUel.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DQ-njyo0.js → theme.BS-DJWat.js} +2 -2
- package/dist/docs/assets/{guides_slack.md.D_xFKlnF.js → guides_slack.md.CjmJSvZS.js} +8 -4
- package/dist/docs/assets/guides_slack.md.CjmJSvZS.lean.js +1 -0
- package/dist/docs/assets/guides_webhooks.md.wSOYas3X.js +183 -0
- package/dist/docs/assets/{guides_webhooks.md.B1EswtUu.lean.js → guides_webhooks.md.wSOYas3X.lean.js} +1 -1
- package/dist/docs/assets/{index.md.Dfv5ic9t.js → index.md.CH_s5uZe.js} +1 -1
- package/dist/docs/assets/{reference_channels.md.DdmiKgqf.js → reference_channels.md.CDhTRfUz.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.DdmiKgqf.lean.js → reference_channels.md.CDhTRfUz.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/slack.html +11 -7
- package/dist/docs/guides/webhooks.html +138 -5
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +5 -5
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +5 -5
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- 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 +19 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +196 -57
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +14 -6
- package/dist/internal/cursor-slack-relay.d.ts +2 -0
- package/dist/internal/cursor-slack-relay.d.ts.map +1 -1
- package/dist/internal/cursor-slack-relay.js +1 -0
- package/dist/internal/deploy-client.d.ts +2 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +1 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +91 -3
- 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 +49 -5
- 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-Bul1WfrB.css +1 -0
- package/dist/playground/assets/index-CkN74U_L.js +87 -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/docs/README.md +1 -0
- package/docs/guides/slack.md +32 -7
- package/docs/guides/webhooks.md +183 -0
- package/docs/reference/channels.md +2 -1
- package/package.json +26 -26
- package/skills/setup-slack/SKILL.md +3 -0
- package/src/ab.ts +7 -0
- package/src/bin/agent-serve.ts +42 -5
- package/src/channels/slack/auth.ts +29 -22
- package/src/channels/slack/cursor-account.ts +2 -0
- package/src/channels/slack/defaults.ts +24 -4
- package/src/channels/slack/dispatch.ts +42 -1
- package/src/channels/slack/external-policy.ts +62 -0
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/interactive.ts +42 -10
- package/src/channels/slack/slack-channel.ts +20 -3
- package/src/channels/slack/types.ts +12 -0
- package/src/internal/ab-fold.ts +4 -0
- package/src/internal/ab-snapshot.ts +1 -0
- package/src/internal/cli-ax.ts +205 -34
- package/src/internal/cursor-slack-relay.ts +3 -0
- package/src/internal/deploy-client.ts +3 -0
- package/src/internal/discovery.ts +8 -1
- package/src/internal/model-pricing.ts +426 -0
- package/src/internal/sdk-runner.ts +10 -2
- package/src/internal/server.ts +9 -5
- 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/docs/assets/guides_slack.md.D_xFKlnF.lean.js +0 -1
- package/dist/docs/assets/guides_webhooks.md.B1EswtUu.js +0 -50
- 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/dist/docs/assets/{index.md.Dfv5ic9t.lean.js → index.md.CH_s5uZe.lean.js} +0 -0
|
@@ -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> {
|
package/src/internal/server.ts
CHANGED
|
@@ -1747,11 +1747,15 @@ async function startCursorSlackRelay(input: {
|
|
|
1747
1747
|
|
|
1748
1748
|
// Optional fields may be undefined — the relay JSON-serializes the
|
|
1749
1749
|
// registration body, which drops them.
|
|
1750
|
-
const agents: CursorSlackAgentRegistration[] = targets.map((target) =>
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1750
|
+
const agents: CursorSlackAgentRegistration[] = targets.map((target) => {
|
|
1751
|
+
const policy = target.accountRuntime.options.policy;
|
|
1752
|
+
return {
|
|
1753
|
+
name: target.agentName,
|
|
1754
|
+
iconUrl: target.accountRuntime.options.agentIcon?.url,
|
|
1755
|
+
iconEmoji: target.accountRuntime.options.agentIcon?.emoji,
|
|
1756
|
+
blockExternals: policy?.blockExternals === false ? false : undefined,
|
|
1757
|
+
};
|
|
1758
|
+
});
|
|
1755
1759
|
|
|
1756
1760
|
const targetByName = new Map(
|
|
1757
1761
|
targets.map((target) => [target.agentName.toLowerCase(), target])
|
|
@@ -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
|
+
}
|