Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| The Spend page says what a workspace spent, where it went and the budgets that hold it, in Workspace under Money: spent at price, agents' spend and the spend limit; by day; by agent, person, channel, model, kind of work and product; budgets from the workspace down to each person, agent and task, with what happens at 100%; the costliest tasks, each with a receipt of every session at the provider's price and what was charged; and how it's priced. Everyone sees what agents spent for them, and owners and billing managers the whole workspace; each person can have a monthly budget agents keep to, and the spend pill in the top bar shows your month or the workspace's against its limit. The spend guide says how. | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| Spend adds up on one ledger. Charged this month has one definition, price less discount, included usage and credit, shared by the plan card, the spend limit, Spend and the top bar; the limit had counted usage not yet closed at full price before the discount, so a comped workspace read as charged a cent. Every agent line on the ledger names the agent and who asked, repository runs by g1t included, so Spent is the sum of its products, Agents is the agent product, and by agent adds up to it; the billing API returns by_agent and by_person. The usage and billing guide says how spend is counted. | 4 | import type { AgentSession, MeterLine, PriceBook, UsageReport } from "@g1t/contracts"; |
| The Spend page says what a workspace spent, where it went and the budgets that hold it, in Workspace under Money: spent at price, agents' spend and the spend limit; by day; by agent, person, channel, model, kind of work and product; budgets from the workspace down to each person, agent and task, with what happens at 100%; the costliest tasks, each with a receipt of every session at the provider's price and what was charged; and how it's priced. Everyone sees what agents spent for them, and owners and billing managers the whole workspace; each person can have a monthly budget agents keep to, and the spend pill in the top bar shows your month or the workspace's against its limit. The spend guide says how. | 5 | |
| Spend adds up on one ledger. Charged this month has one definition, price less discount, included usage and credit, shared by the plan card, the spend limit, Spend and the top bar; the limit had counted usage not yet closed at full price before the discount, so a comped workspace read as charged a cent. Every agent line on the ledger names the agent and who asked, repository runs by g1t included, so Spent is the sum of its products, Agents is the agent product, and by agent adds up to it; the billing API returns by_agent and by_person. The usage and billing guide says how spend is counted. | 6 | import { agentMicros, agentRateLabel, agentSpendHref, attributionSlices, daySeries, markupLabel, percentLabel, pricingOf, readPeriod, readScope, receiptOf, spanFor, tokenCount } from "./spend.ts"; |
| The Spend page says what a workspace spent, where it went and the budgets that hold it, in Workspace under Money: spent at price, agents' spend and the spend limit; by day; by agent, person, channel, model, kind of work and product; budgets from the workspace down to each person, agent and task, with what happens at 100%; the costliest tasks, each with a receipt of every session at the provider's price and what was charged; and how it's priced. Everyone sees what agents spent for them, and owners and billing managers the whole workspace; each person can have a monthly budget agents keep to, and the spend pill in the top bar shows your month or the workspace's against its limit. The spend guide says how. | 7 | |
| 8 | const OCT_8 = new Date("2026-10-08T15:00:00Z"); | |
| 9 | ||
| 10 | test("a period's days match the agents service's, both ends included", () => { | |
| 11 | assert.deepEqual(spanFor("month", OCT_8), { from: "2026-10-01", until: "2026-10-08" }); | |
| 12 | assert.deepEqual(spanFor("last_month", OCT_8), { from: "2026-09-01", until: "2026-09-30" }); | |
| 13 | assert.deepEqual(spanFor("last_month", new Date("2026-01-15T00:00:00Z")), { from: "2025-12-01", until: "2025-12-31" }); | |
| 14 | assert.deepEqual(spanFor("7d", OCT_8), { from: "2026-10-02", until: "2026-10-08" }); | |
| 15 | assert.deepEqual(spanFor("30d", OCT_8), { from: "2026-09-09", until: "2026-10-08" }); | |
| 16 | }); | |
| 17 | ||
| 18 | test("only someone who may see the workspace's spend gets it; anything else is their own", () => { | |
| 19 | assert.equal(readScope(null, true), "workspace"); | |
| 20 | assert.equal(readScope("me", true), "me"); | |
| 21 | assert.equal(readScope("workspace", false), "me"); | |
| 22 | assert.equal(readPeriod("7d"), "7d"); | |
| 23 | assert.equal(readPeriod("year"), "month"); | |
| 24 | }); | |
| 25 | ||
| 26 | test("a day series has every day, products added together, zeros where nothing was spent", () => { | |
| 27 | const days = daySeries("2026-10-01", "2026-10-03", [ | |
| 28 | { day: "2026-10-01", micros: 5 }, | |
| 29 | { day: "2026-10-01", micros: 7 }, | |
| 30 | { day: "2026-10-03", micros: 1 }, | |
| 31 | ]); | |
| 32 | assert.deepEqual(days, [ | |
| 33 | { day: "2026-10-01", micros: 12 }, | |
| 34 | { day: "2026-10-02", micros: 0 }, | |
| 35 | { day: "2026-10-03", micros: 1 }, | |
| 36 | ]); | |
| 37 | }); | |
| 38 | ||
| Pricing says it plainly: models at the provider's price, the agent rate for what g1t runs around every model call (the gateway, secrets, routing, context and pass-through to your own provider, so your own keys too), everything else at cost plus 20%, your own runners free, no seats; Security and quality comes with the plan with no separate fee, and live activations end. Each agent has an effort setting, Auto to Max, with what a typical task has cost at each level, and Spend's Spend less, keep quality suggests a lower level only when the agent's own past work shows quality held, to apply or dismiss. The pricing, spend and agents guides say how. | 39 | test("pricing comes from the price book: models at the provider's price, what g1t runs at its markup", () => { |
| The Spend page says what a workspace spent, where it went and the budgets that hold it, in Workspace under Money: spent at price, agents' spend and the spend limit; by day; by agent, person, channel, model, kind of work and product; budgets from the workspace down to each person, agent and task, with what happens at 100%; the costliest tasks, each with a receipt of every session at the provider's price and what was charged; and how it's priced. Everyone sees what agents spent for them, and owners and billing managers the whole workspace; each person can have a monthly budget agents keep to, and the spend pill in the top bar shows your month or the workspace's against its limit. The spend guide says how. | 40 | const book = { |
| 41 | modelMarginPercent: 0, | |
| 42 | changes: [], | |
| 43 | prices: [ | |
| 44 | { meter: "sandbox_second", costMicros: 10, markupPercent: 20, priceMicros: 12 }, | |
| 45 | { meter: "build_second", costMicros: 10, markupPercent: 20, priceMicros: 12 }, | |
| Pricing says it plainly: models at the provider's price, the agent rate for what g1t runs around every model call (the gateway, secrets, routing, context and pass-through to your own provider, so your own keys too), everything else at cost plus 20%, your own runners free, no seats; Security and quality comes with the plan with no separate fee, and live activations end. Each agent has an effort setting, Auto to Max, with what a typical task has cost at each level, and Spend's Spend less, keep quality suggests a lower level only when the agent's own past work shows quality held, to apply or dismiss. The pricing, spend and agents guides say how. | 46 | { meter: "agent_models", costMicros: 1_000_000, markupPercent: 0, priceMicros: 1_000_000 }, |
| 47 | { meter: "gateway_models", costMicros: 1_000_000, markupPercent: 0, priceMicros: 1_000_000 }, | |
| 48 | { meter: "card_fee_percent", costMicros: 29_000, markupPercent: 0, priceMicros: 29_000 }, | |
| 49 | { meter: "agent_token_weight_input", costMicros: 1_000_000, markupPercent: 0, priceMicros: 1_000_000 }, | |
| 50 | { meter: "agent_tokens", costMicros: 0, markupPercent: 0, priceMicros: 0 }, | |
| 51 | { meter: "agent_tokens_own", costMicros: 0, markupPercent: 0, priceMicros: 0 }, | |
| The Spend page says what a workspace spent, where it went and the budgets that hold it, in Workspace under Money: spent at price, agents' spend and the spend limit; by day; by agent, person, channel, model, kind of work and product; budgets from the workspace down to each person, agent and task, with what happens at 100%; the costliest tasks, each with a receipt of every session at the provider's price and what was charged; and how it's priced. Everyone sees what agents spent for them, and owners and billing managers the whole workspace; each person can have a monthly budget agents keep to, and the spend pill in the top bar shows your month or the workspace's against its limit. The spend guide says how. | 52 | ], |
| 53 | } as unknown as PriceBook; | |
| Pricing says it plainly: models at the provider's price, the agent rate for what g1t runs around every model call (the gateway, secrets, routing, context and pass-through to your own provider, so your own keys too), everything else at cost plus 20%, your own runners free, no seats; Security and quality comes with the plan with no separate fee, and live activations end. Each agent has an effort setting, Auto to Max, with what a typical task has cost at each level, and Spend's Spend less, keep quality suggests a lower level only when the agent's own past work shows quality held, to apply or dismiss. The pricing, spend and agents guides say how. | 54 | (book as { changes: unknown[] }).changes = [{ meter: "agent_tokens", newCostMicros: 250_000, effectiveAt: "2026-10-22T00:00:00Z" }]; |
| The Spend page says what a workspace spent, where it went and the budgets that hold it, in Workspace under Money: spent at price, agents' spend and the spend limit; by day; by agent, person, channel, model, kind of work and product; budgets from the workspace down to each person, agent and task, with what happens at 100%; the costliest tasks, each with a receipt of every session at the provider's price and what was charged; and how it's priced. Everyone sees what agents spent for them, and owners and billing managers the whole workspace; each person can have a monthly budget agents keep to, and the spend pill in the top bar shows your month or the workspace's against its limit. The spend guide says how. | 55 | const pricing = pricingOf(book); |
| 56 | assert.equal(pricing.modelMarkupPercent, 0); | |
| Pricing says it plainly: models at the provider's price, the agent rate for what g1t runs around every model call (the gateway, secrets, routing, context and pass-through to your own provider, so your own keys too), everything else at cost plus 20%, your own runners free, no seats; Security and quality comes with the plan with no separate fee, and live activations end. Each agent has an effort setting, Auto to Max, with what a typical task has cost at each level, and Spend's Spend less, keep quality suggests a lower level only when the agent's own past work shows quality held, to apply or dismiss. The pricing, spend and agents guides say how. | 57 | // Models, the gateway, card fees and weights are not things g1t runs: the markup is one figure. |
| The Spend page says what a workspace spent, where it went and the budgets that hold it, in Workspace under Money: spent at price, agents' spend and the spend limit; by day; by agent, person, channel, model, kind of work and product; budgets from the workspace down to each person, agent and task, with what happens at 100%; the costliest tasks, each with a receipt of every session at the provider's price and what was charged; and how it's priced. Everyone sees what agents spent for them, and owners and billing managers the whole workspace; each person can have a monthly budget agents keep to, and the spend pill in the top bar shows your month or the workspace's against its limit. The spend guide says how. | 58 | assert.deepEqual(pricing.markup, { min: 20, max: 20 }); |
| 59 | assert.equal(markupLabel(pricing.markup!), "20%"); | |
| 60 | assert.equal(markupLabel({ min: 15, max: 20 }), "15–20%"); | |
| Pricing says it plainly: models at the provider's price, the agent rate for what g1t runs around every model call (the gateway, secrets, routing, context and pass-through to your own provider, so your own keys too), everything else at cost plus 20%, your own runners free, no seats; Security and quality comes with the plan with no separate fee, and live activations end. Each agent has an effort setting, Auto to Max, with what a typical task has cost at each level, and Spend's Spend less, keep quality suggests a lower level only when the agent's own past work shows quality held, to apply or dismiss. The pricing, spend and agents guides say how. | 61 | // The agent rate, $0 until its dated version starts. |
| 62 | assert.equal(pricing.agentRateMicros, 0); | |
| 63 | assert.deepEqual(pricing.agentRateComing, { micros: 250_000, from: "2026-10-22" }); | |
| 64 | const dollars = (m: number) => `$${(m / 1_000_000).toFixed(2)}`; | |
| 65 | assert.equal(agentRateLabel(pricing, dollars), "$0.25 per million tokens from 2026-10-22"); | |
| 66 | assert.equal(agentRateLabel({ agentRateMicros: 250_000, agentRateComing: null }, dollars), "$0.25 per million tokens"); | |
| The Spend page says what a workspace spent, where it went and the budgets that hold it, in Workspace under Money: spent at price, agents' spend and the spend limit; by day; by agent, person, channel, model, kind of work and product; budgets from the workspace down to each person, agent and task, with what happens at 100%; the costliest tasks, each with a receipt of every session at the provider's price and what was charged; and how it's priced. Everyone sees what agents spent for them, and owners and billing managers the whole workspace; each person can have a monthly budget agents keep to, and the spend pill in the top bar shows your month or the workspace's against its limit. The spend guide says how. | 67 | }); |
| 68 | ||
| 69 | const session = (over: Partial<AgentSession>): AgentSession => | |
| 70 | ({ id: "s", parent_id: null, root_id: "r", charged_micros: 0, cost_micros: 0, input_tokens: 0, output_tokens: 0, steps: 0, tool_calls: 0, created_at: "2026-10-08T00:00:00Z", ...over }) as AgentSession; | |
| 71 | ||
| 72 | test("a receipt gives the root its own share, children under their parents, and the tree's totals", () => { | |
| 73 | const tree = [ | |
| 74 | session({ id: "r", charged_micros: 1_840_000, cost_micros: 1_000_000, input_tokens: 400_000, output_tokens: 30_000, steps: 6, tool_calls: 20 }), | |
| 75 | session({ id: "c1", parent_id: "r", charged_micros: 300_000, cost_micros: 200_000, input_tokens: 10_000, steps: 2, created_at: "2026-10-08T00:01:00Z" }), | |
| 76 | session({ id: "g1", parent_id: "c1", charged_micros: 40_000, cost_micros: 30_000, steps: 1, created_at: "2026-10-08T00:02:00Z" }), | |
| 77 | ]; | |
| 78 | const receipt = receiptOf(tree, "r")!; | |
| 79 | assert.deepEqual( | |
| 80 | receipt.lines.map((l) => [l.session.id, l.depth, l.ownMicros]), | |
| 81 | [ | |
| 82 | ["r", 0, 1_500_000], | |
| 83 | ["c1", 1, 300_000], | |
| 84 | ["g1", 2, 40_000], | |
| 85 | ], | |
| 86 | ); | |
| 87 | assert.equal(receipt.chargedMicros, 1_840_000); | |
| 88 | assert.equal(receipt.providerMicros, 1_230_000); | |
| 89 | assert.equal(receipt.steps, 9); | |
| 90 | assert.equal(receiptOf([], "r"), null); | |
| 91 | }); | |
| 92 | ||
| 93 | test("token counts and percents read short", () => { | |
| 94 | assert.equal(tokenCount(830), "830"); | |
| 95 | assert.equal(tokenCount(1_200), "1.2K"); | |
| 96 | assert.equal(tokenCount(412_000), "412K"); | |
| 97 | assert.equal(tokenCount(1_250_000), "1.3M"); | |
| 98 | assert.equal(percentLabel(62, 100), "62%"); | |
| 99 | assert.equal(percentLabel(62, null), ""); | |
| 100 | }); | |
| Spend adds up on one ledger. Charged this month has one definition, price less discount, included usage and credit, shared by the plan card, the spend limit, Spend and the top bar; the limit had counted usage not yet closed at full price before the discount, so a comped workspace read as charged a cent. Every agent line on the ledger names the agent and who asked, repository runs by g1t included, so Spent is the sum of its products, Agents is the agent product, and by agent adds up to it; the billing API returns by_agent and by_person. The usage and billing guide says how spend is counted. | 101 | |
| 102 | /** A usage report as billing answers it for a comped workspace in October: every line discounted in full, attributed to its agent. */ | |
| 103 | function octoberReport(): Pick<UsageReport, "totals" | "products" | "byAgent" | "byPerson"> { | |
| 104 | const meter = (key: string, product: string, micros: number): MeterLine => ({ key, label: key, product, unit: "entries", quantity: 1, micros, daily: [micros], byProject: [] }); | |
| 105 | const products = [ | |
| 106 | { key: "agent", label: "Agent", micros: 2_544_000, meters: [meter("agent_models", "agent", 1_900_000), meter("agent_rate", "agent", 400_000), meter("agent_sandbox", "agent", 244_000)] }, | |
| 107 | { key: "sandboxes", label: "Sandboxes", micros: 3_360_000, meters: [meter("sandbox", "sandboxes", 3_360_000)] }, | |
| 108 | { key: "deployments", label: "Deployments", micros: 4_480_000, meters: [meter("requests", "deployments", 4_480_000)] }, | |
| 109 | ]; | |
| 110 | return { | |
| 111 | totals: { priceMicros: 10_384_000, discountMicros: 10_384_000, includedMicros: 0, creditsMicros: 0, chargedMicros: 0, pendingMicros: 0, costMicros: 8_650_000 }, | |
| 112 | products, | |
| 113 | byAgent: [ | |
| 114 | { key: "g1t", label: "@g1t", micros: 2_520_000, count: 9 }, | |
| 115 | { key: "mike", label: "@mike", micros: 20_000, count: 2 }, | |
| 116 | { key: "margo", label: "@margo", micros: 4_000, count: 1 }, | |
| 117 | ], | |
| 118 | byPerson: [ | |
| 119 | { key: "chase", label: "@chase", micros: 2_540_000, count: 11 }, | |
| 120 | { key: "", label: "No one asked", micros: 4_000, count: 1 }, | |
| 121 | ], | |
| 122 | }; | |
| 123 | } | |
| 124 | ||
| 125 | test("Home's Spent, Spend's tiles and the top bar's Agents reconcile on the one ledger", () => { | |
| 126 | const report = octoberReport(); | |
| 127 | // Spent: every product at price, which is the report's total. | |
| 128 | const spent = report.products.reduce((n, p) => n + p.micros, 0); | |
| 129 | assert.equal(spent, report.totals.priceMicros); | |
| 130 | // Agents: the agent product, part of Spent, and what the agents by handle add up to. | |
| 131 | assert.equal(agentMicros(report), 2_544_000); | |
| 132 | assert.ok(agentMicros(report) <= spent); | |
| 133 | const slices = attributionSlices(report); | |
| 134 | assert.ok(slices); | |
| 135 | assert.equal( | |
| 136 | slices.agent.reduce((n, s) => n + s.micros, 0), | |
| 137 | agentMicros(report), | |
| 138 | ); | |
| 139 | assert.equal( | |
| 140 | slices.person.reduce((n, s) => n + s.micros, 0), | |
| 141 | agentMicros(report), | |
| 142 | ); | |
| 143 | assert.ok(slices.agent.every((s) => s.micros <= agentMicros(report))); | |
| 144 | // Charged: nothing, on a 100% discount, however much was spent. | |
| 145 | assert.equal(report.totals.chargedMicros, 0); | |
| 146 | assert.equal(report.totals.priceMicros - report.totals.discountMicros - report.totals.includedMicros - report.totals.creditsMicros, 0); | |
| 147 | }); | |
| 148 | ||
| 149 | test("an agent's slice opens its own Spend tab; work attributed to no one opens nothing", () => { | |
| 150 | const slices = attributionSlices(octoberReport()); | |
| 151 | assert.ok(slices); | |
| 152 | assert.equal(agentSpendHref("flagon-io", slices.agent[0]), "/flagon-io/-/agents/g1t/spend"); | |
| 153 | assert.equal(agentSpendHref("flagon-io", slices.person[1]), null); | |
| 154 | assert.deepEqual(slices.agent[1], { key: "mike", label: "@mike", micros: 20_000, count: 2 }); | |
| 155 | // A report from before attribution has no slices: the caller falls back. | |
| 156 | assert.equal(attributionSlices({ byAgent: undefined, byPerson: undefined }), null); | |
| 157 | assert.equal(attributionSlices(null), null); | |
| 158 | assert.equal(agentMicros({ products: [] }), 0); | |
| 159 | }); |