@broberg/ai-sdk 0.62.2 → 0.63.0

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/README.md CHANGED
@@ -221,8 +221,15 @@ You don't have to wire a sink. If `UPMETRICS_API_KEY` is in the env, a bare
221
221
  call-sites passed no sink, leaving ~91% of Mistral spend invisible. No key in
222
222
  the env → no sink, no crash (ship-dark).
223
223
 
224
+ **Every client needs a name (0.63+, F034.4).** `createAI()` throws at startup unless
225
+ `UPMETRICS_AGENT_NAME` is set or you pass `agentName` — even when no key is present, so
226
+ your tests catch it before production does. Owner order 2026-10-15: «Ingen må være
227
+ anonyme» — measured over 30 days, 8 repos had sent their spend as `unknown`. Not needed
228
+ with an explicit `costSink` or `costSink: null`.
229
+
224
230
  ```ts
225
- createAI(); // key in env → tracked; no key → nothing happens
231
+ createAI({ agentName: "my-app" }); // key in env → tracked; no key → nothing happens
232
+ createAI(); // same, if UPMETRICS_AGENT_NAME is set — else throws
226
233
  createAI({ costSink: mySink }); // explicit sink wins
227
234
  createAI({ costSink: null }); // explicit OPT-OUT — see below
228
235
  ```
@@ -230,7 +237,7 @@ createAI({ costSink: null }); // explicit OPT-OUT — see below
230
237
  | Env var | Effect |
231
238
  |---|---|
232
239
  | `UPMETRICS_API_KEY` | **The only switch.** Present → tracking on. |
233
- | `UPMETRICS_AGENT_NAME` | Row label. Set it — the fallback is `npm_package_name`, which is empty for a process not started via an npm/bun script (a `node dist/…` or Docker service logs as `unknown`). |
240
+ | `UPMETRICS_AGENT_NAME` | Row label, **required** (or `agentName`) since 0.63.0 — set it in production AND in your test env. Up to 0.62 the fallback was `npm_package_name`, empty outside an npm/bun script, so `node dist/…` and Docker services logged as `unknown`. |
234
241
  | `UPMETRICS_BASE_URL` | Ingest host. Defaults to `https://upmetrics.org`. |
235
242
  | `UPMETRICS_COMPLIANCE` | `1` sets compliance mode. `Usage` carries no prompt/response text either way. |
236
243
 
package/dist/index.d.ts CHANGED
@@ -1864,6 +1864,9 @@ declare const aiConfigSchema: z.ZodObject<{
1864
1864
  * to opt OUT explicitly — for a consumer that already reports its own costs
1865
1865
  * and would otherwise be counted twice (F034.3). An object is used as-is. */
1866
1866
  costSink: z.ZodOptional<z.ZodNullable<z.ZodType<CostSink, z.ZodTypeDef, CostSink>>>;
1867
+ /** F034.4 — who is calling, as Upmetrics groups it (e.g. "cms"). Required when
1868
+ * `costSink` is omitted, unless UPMETRICS_AGENT_NAME is set. */
1869
+ agentName: z.ZodOptional<z.ZodString>;
1867
1870
  budget: z.ZodOptional<z.ZodObject<{
1868
1871
  perCallUsd: z.ZodOptional<z.ZodNumber>;
1869
1872
  rollingUsd: z.ZodOptional<z.ZodNumber>;
@@ -1896,6 +1899,7 @@ declare const aiConfigSchema: z.ZodObject<{
1896
1899
  transport: "http" | "subprocess";
1897
1900
  }>> | undefined;
1898
1901
  providers?: Record<string, ProviderAdapter> | undefined;
1902
+ agentName?: string | undefined;
1899
1903
  budget?: {
1900
1904
  perCallUsd?: number | undefined;
1901
1905
  rollingUsd?: number | undefined;
@@ -1916,6 +1920,7 @@ declare const aiConfigSchema: z.ZodObject<{
1916
1920
  transport: "http" | "subprocess";
1917
1921
  }>> | undefined;
1918
1922
  providers?: Record<string, ProviderAdapter> | undefined;
1923
+ agentName?: string | undefined;
1919
1924
  budget?: {
1920
1925
  perCallUsd?: number | undefined;
1921
1926
  rollingUsd?: number | undefined;
@@ -2334,8 +2339,8 @@ declare const falStubAdapter: ProviderAdapter;
2334
2339
  * wires the live adapters. */
2335
2340
  declare const stubProviders: Record<string, ProviderAdapter>;
2336
2341
 
2337
- declare const VERSION: "0.62.2";
2338
- declare const SDK_TAG: "@broberg/ai-sdk@0.62.2";
2342
+ declare const VERSION: "0.63.0";
2343
+ declare const SDK_TAG: "@broberg/ai-sdk@0.63.0";
2339
2344
 
2340
2345
  /** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
2341
2346
  * per-call override.
package/dist/index.js CHANGED
@@ -3910,6 +3910,9 @@ var aiConfigSchema = z.object({
3910
3910
  * to opt OUT explicitly — for a consumer that already reports its own costs
3911
3911
  * and would otherwise be counted twice (F034.3). An object is used as-is. */
3912
3912
  costSink: z.custom().nullable().optional(),
3913
+ /** F034.4 — who is calling, as Upmetrics groups it (e.g. "cms"). Required when
3914
+ * `costSink` is omitted, unless UPMETRICS_AGENT_NAME is set. */
3915
+ agentName: z.string().optional(),
3913
3916
  budget: budgetSchema.optional(),
3914
3917
  availability: availabilitySchema.optional()
3915
3918
  });
@@ -3918,8 +3921,8 @@ var aiConfigSchema = z.object({
3918
3921
  import { randomUUID } from "crypto";
3919
3922
 
3920
3923
  // src/version.ts
3921
- var VERSION = "0.62.2";
3922
- var SDK_TAG = "@broberg/ai-sdk@0.62.2";
3924
+ var VERSION = "0.63.0";
3925
+ var SDK_TAG = "@broberg/ai-sdk@0.63.0";
3923
3926
 
3924
3927
  // src/cost/sinks/upmetrics.ts
3925
3928
  function isRetryableStatus(status) {
@@ -3929,6 +3932,7 @@ var MAX_QUEUE = 30;
3929
3932
  var MAX_ATTEMPTS = 5;
3930
3933
  var MAX_BACKOFF_MS = 3e4;
3931
3934
  function upmetricsSink(config) {
3935
+ if (!config.agentName?.trim()) throw new Error("upmetricsSink(): agentName is required (e.g. your repo name) \u2014 anonymous cost rows are not allowed");
3932
3936
  const doFetch = config.fetch ?? fetch;
3933
3937
  const url = `${config.baseUrl.replace(/\/$/, "")}/api/agent`;
3934
3938
  const retry = config.retry ?? true;
@@ -4258,21 +4262,28 @@ var ROUTED_SPECS = [
4258
4262
  DEFAULT_BATCH_SPEC,
4259
4263
  DEFAULT_TRANSCRIBE_SPEC
4260
4264
  ];
4261
- function defaultCostSink() {
4265
+ function defaultCostSink(agentName) {
4262
4266
  const apiKey = process.env.UPMETRICS_API_KEY;
4263
4267
  if (!apiKey) return void 0;
4264
4268
  return upmetricsSink({
4265
4269
  baseUrl: process.env.UPMETRICS_BASE_URL ?? "https://upmetrics.org",
4266
4270
  apiKey,
4267
- agentName: process.env.UPMETRICS_AGENT_NAME ?? process.env.npm_package_name ?? "unknown",
4271
+ agentName,
4268
4272
  complianceMode: process.env.UPMETRICS_COMPLIANCE === "1"
4269
4273
  });
4270
4274
  }
4275
+ function requireAgentName(configured) {
4276
+ const name = (configured ?? process.env.UPMETRICS_AGENT_NAME ?? "").trim();
4277
+ if (name) return name;
4278
+ throw new Error(
4279
+ 'createAI(): no agent name \u2014 every AI call must say who made it (owner order 2026-10-15). Set UPMETRICS_AGENT_NAME=<your repo> (production AND test env), or pass createAI({ agentName: "<your repo>" }). Not needed with an explicit costSink, or costSink: null.'
4280
+ );
4281
+ }
4271
4282
  function createAI(config = {}) {
4272
4283
  const cfg = aiConfigSchema.parse(config);
4273
4284
  if (cfg.byok) assertByokProviders(cfg.providers);
4274
4285
  const providers = cfg.providers ?? defaultProviders;
4275
- const costSink = cfg.costSink === void 0 ? defaultCostSink() : cfg.costSink ?? void 0;
4286
+ const costSink = cfg.costSink === void 0 ? defaultCostSink(requireAgentName(cfg.agentName)) : cfg.costSink ?? void 0;
4276
4287
  const budget = cfg.budget ? new BudgetGuard(cfg.budget) : void 0;
4277
4288
  const estTokens = (s) => Math.ceil(s.length / 4);
4278
4289
  async function preflight(spec, estInTokens, estOutTokens) {