Skip to content
125 linesCodeBlameRaw

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/**
2 * Spend's reads: each part from the service that owns it, each on its own
3 * so one that fails leaves only its part saying so. Billing has the
4 * workspace's money (usage at price, the spend limit, caps, the price
5 * book); the agents service has where agent work went (by agent, person,
6 * channel, model, kind) and the budgets below the workspace's (its agents
7 * together, each person, each agent, each session). The shaping is in
8 * ./spend.ts.
9 */
10import type { AgentPolicy, AgentSpendBreakdown, Limit, PersonBudgets, SpendPeriod, UsageReport, User, WorkspaceAgent } from "@g1t/contracts";
11
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.12import { type Pricing, type SpendScope, agentMicros, attributionSlices, pricingOf, spanFor, spentMicros } from "./spend";
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.13import { billing, workspaceAgents } from "./services.server";
14
15const warn = (what: string) => (error: unknown) => {
16 console.warn(`spend: ${what} failed`, error);
17 return null;
18};
19
20const value = <T>(result: { ok: true; value: T } | { ok: false } | null): T | null => (result && result.ok ? result.value : null);
21
22/** Where agent work went for the scope and period: everyone's, or only what the viewer asked for. */
23export function loadBreakdown(viewer: User, slug: string, scope: SpendScope, period: SpendPeriod): Promise<AgentSpendBreakdown | null> {
24 return workspaceAgents
25 .spend(slug, viewer, null, { period, person: scope === "me" ? viewer.username.toLowerCase() : null })
26 .then(value)
27 .catch(warn("agents spend"));
28}
29
30/** Everything the workspace used over the period, at price, by product and day. */
31export function loadUsage(viewer: User, slug: string, period: SpendPeriod, now: Date): Promise<UsageReport | null> {
32 const { from, until } = spanFor(period, now);
33 return billing.usageReport(slug, viewer, { from, until }).then(value).catch(warn("usage report"));
34}
35
36/** One agent's budget and month, as the budgets list shows it. */
Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent.37export type AgentBudgetRow = Pick<WorkspaceAgent, "id" | "handle" | "display_name" | "avatar_seed" | "look" | "budget" | "spent_month_micros" | "builtin">;
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.38
39/** Every level of budget, widest first, with what is spent against each this month. */
40export type Budgets = {
41 /** The workspace's spend limit (billing); null when billing did not answer or is off. */
42 limit: Limit | null;
43 /** The plan's caps on one run and one issue (billing). */
44 caps: { runMicros: number; issueMicros: number } | null;
45 /** The workspace's agent policy: every agent together, the per-person default, a new agent's, a session's. */
46 policy: AgentPolicy | null;
47 /** Every agent's spend this month, against the policy's budget. */
48 agentsMonthMicros: number | null;
49 people: PersonBudgets | null;
50 agents: AgentBudgetRow[] | null;
51};
52
53export async function loadBudgets(viewer: User, slug: string, monthBreakdown: Promise<AgentSpendBreakdown | null>): Promise<Budgets> {
54 const [limit, entitlements, policy, people, agents, month] = await Promise.all([
55 billing.limit(slug, viewer).then(value).catch(warn("limit")),
56 billing.entitlements(slug).catch(warn("entitlements")),
57 workspaceAgents.policy(slug, viewer).then(value).catch(warn("agent policy")),
58 workspaceAgents.personBudgets(slug, viewer).then(value).catch(warn("person budgets")),
59 workspaceAgents.list(slug, viewer).then(value).catch(warn("agents")),
60 monthBreakdown,
61 ]);
62 return {
63 limit,
64 caps: entitlements ? { runMicros: entitlements.runCapMicros, issueMicros: entitlements.issueCapMicros } : null,
65 policy,
66 agentsMonthMicros: month?.total_micros ?? null,
67 people,
68 agents: agents
69 ? agents
70 .filter((a) => !a.archived_at)
Agents have faces, and are never mistaken for people. Every agent wears a little bot face drawn from a look it owns, shape, colour, eyes, mouth, antenna, accessory and pattern, chosen in its builder and on its Profile tab with a live preview, Shuffle and a way back to the face its seed gives it; the face blinks on its own time, breathes, narrows its eyes while the agent works, shuts them asleep and bounces when it finishes, all of it still for anyone who asked for less motion. Wherever an agent shows, in chat, in a list, on a mention, on a review or a commit, its avatar carries an agent marker, and the people reading it are told so. In Chat, direct messages are two lists: People, and Agents, which also holds the agents you haven't talked to yet; a conversation with both a person and an agent in it is marked in the list, named in the conversation's header, spelled out by the composer and explained once the first time it opens. Agents keep their look in the agents service, which every service passes along. The chat and agents guides say so, and CONTRIBUTING makes the shared avatar the only way to draw an agent.71 .map((a) => ({ id: a.id, handle: a.handle, display_name: a.display_name, avatar_seed: a.avatar_seed, look: a.look ?? null, budget: a.budget, spent_month_micros: a.spent_month_micros, builtin: a.builtin }))
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.72 .sort((a, b) => Number(b.builtin) - Number(a.builtin) || b.spent_month_micros - a.spent_month_micros)
73 : null,
74 };
75}
76
77/** What the price book says about pricing. */
78export function loadPricing(): Promise<Pricing | null> {
79 return billing
80 .prices()
81 .then(pricingOf)
82 .catch(warn("prices"));
83}
84
85/** The top bar's pill: the viewer's month, and the workspace's for someone who may see it. */
86export type PillData = {
87 month: string;
88 me: { spentMicros: number; budgetMicros: number | null; byKind: AgentSpendBreakdown["by_kind"]; byAgent: AgentSpendBreakdown["by_agent"] } | null;
The top bar's spend pill counts both tabs the same way, at price: You is what agents did for you, Workspace is everything the workspace used this month, the figure Home and Spend show, with what it was charged after its plan and credit underneath, against its spend limit; so a comped workspace reads as what it used and nothing charged, not the other way round. The spend guide says so.89 /**
90 * The workspace's month: everything it used at price (the figure Home and
91 * Spend show), what it was charged after its plan and credit (the figure
92 * its spend limit governs), its limit, and its agents' share.
93 */
94 workspace: { spentMicros: number | null; chargedMicros: number | null; limitMicros: number | null; agentsMicros: number | null; byAgent: AgentSpendBreakdown["by_agent"] } | null;
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.95};
96
97export async function loadPill(viewer: User, slug: string, mayWorkspace: boolean): Promise<PillData> {
98 const me = viewer.username.toLowerCase();
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.99 const [mine, people, everyone, limit, usage] = await Promise.all([
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.100 loadBreakdown(viewer, slug, "me", "month"),
101 workspaceAgents.personBudgets(slug, viewer).then(value).catch(warn("person budgets")),
102 mayWorkspace ? loadBreakdown(viewer, slug, "workspace", "month") : Promise.resolve(null),
103 mayWorkspace ? billing.limit(slug, viewer).then(value).catch(warn("limit")) : Promise.resolve(null),
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.104 // The workspace's agents figure is the agent product on billing's
105 // ledger, the same ledger "charged" is read from; the agents service's
106 // own count stands in only while billing has not answered.
The top bar's spend pill counts both tabs the same way, at price: You is what agents did for you, Workspace is everything the workspace used this month, the figure Home and Spend show, with what it was charged after its plan and credit underneath, against its spend limit; so a comped workspace reads as what it used and nothing charged, not the other way round. The spend guide says so.107 mayWorkspace ? loadUsage(viewer, slug, "month", new Date()) : Promise.resolve(null),
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.108 ]);
109 const own = people?.people.find((p) => p.username === me);
110 const budget = own ? own.monthly_micros : (people?.default_micros ?? null);
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.111 const attributed = attributionSlices(usage);
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.112 return {
113 month: (mine ?? everyone)?.period ?? new Date().toISOString().slice(0, 7),
114 me: mine ? { spentMicros: mine.total_micros, budgetMicros: budget, byKind: mine.by_kind, byAgent: mine.by_agent } : null,
115 workspace: mayWorkspace
116 ? {
Money is written one way. A single formatter turns millionths of a dollar into dollars, rounding half up on whole micros rather than on a float, so the same sum reads the same on every page: under a cent reads <$0.01 on a total and exactly nothing is $0.00, while the statement's lines, a session's receipt, the price book and an agent's effort costs carry up to four places where the fraction of a cent is the point; the six formatters that each rounded their own way are gone. This month is the calendar month in UTC from its first day to today everywhere, and billing counts the month's not-yet-closed usage in any range that reaches into the current month, so the top bar's pill, Spend, Home and Usage ask for the same days and get the same figure; Home now reads the usage report the others read instead of adding up statements. Usage's pending sentence says what of that usage the close will charge after the discount and included usage, which the billing API returns as pending_charged_micros. A reconciliation test holds the pill, Spend, Home and Usage to one number for one month. The usage and billing guide says how amounts are written and what this month means.117 spentMicros: usage ? spentMicros(usage) : null,
The top bar's spend pill counts both tabs the same way, at price: You is what agents did for you, Workspace is everything the workspace used this month, the figure Home and Spend show, with what it was charged after its plan and credit underneath, against its spend limit; so a comped workspace reads as what it used and nothing charged, not the other way round. The spend guide says so.118 chargedMicros: limit?.spentMicros ?? null,
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.119 limitMicros: limit ? (limit.spendLimitMicros ?? limit.ceilingMicros) : null,
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.120 agentsMicros: usage ? agentMicros(usage) : (everyone?.total_micros ?? null),
121 byAgent: attributed?.agent ?? everyone?.by_agent ?? [],
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.122 }
123 : null,
124 };
125}