@agent-native/core 0.187.0 → 0.187.1-nightly-20260924193134

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.
Files changed (46) hide show
  1. package/dist/agent/engine/builder-engine.js +11 -0
  2. package/dist/agent/engine/types.d.ts +1 -0
  3. package/dist/agent/production-agent.d.ts +2 -0
  4. package/dist/agent/production-agent.js +28 -1
  5. package/dist/agent/run-loop-with-resume.js +6 -0
  6. package/dist/automation/index.d.ts +1 -1
  7. package/dist/client/error-format.js +4 -4
  8. package/dist/client/settings/UsageSection.js +105 -17
  9. package/dist/collab/routes.d.ts +1 -1
  10. package/dist/feature-flags/index.d.ts +1 -1
  11. package/dist/feature-flags/index.js +1 -1
  12. package/dist/feature-flags/registry.d.ts +1 -0
  13. package/dist/feature-flags/registry.js +5 -0
  14. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/integrations/webhook-handler.js +4 -1
  18. package/dist/jobs/background-automation-runner.js +6 -1
  19. package/dist/localization/core-messages/ar-SA.js +9 -1
  20. package/dist/localization/core-messages/de-DE.js +9 -1
  21. package/dist/localization/core-messages/en-US.d.ts +9 -1
  22. package/dist/localization/core-messages/en-US.js +9 -1
  23. package/dist/localization/core-messages/es-ES.js +9 -1
  24. package/dist/localization/core-messages/fr-FR.js +9 -1
  25. package/dist/localization/core-messages/hi-IN.js +9 -1
  26. package/dist/localization/core-messages/ja-JP.js +9 -1
  27. package/dist/localization/core-messages/ko-KR.js +9 -1
  28. package/dist/localization/core-messages/pt-BR.js +9 -1
  29. package/dist/localization/core-messages/zh-CN.js +9 -1
  30. package/dist/localization/core-messages/zh-TW.js +9 -1
  31. package/dist/localization/core-messages.d.ts +9 -1
  32. package/dist/notifications/routes.d.ts +3 -3
  33. package/dist/observability/routes.d.ts +3 -3
  34. package/dist/org/handlers.d.ts +1 -1
  35. package/dist/secrets/routes.d.ts +9 -9
  36. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  37. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  38. package/dist/server/agent-teams.js +4 -1
  39. package/dist/server/core-routes-plugin.d.ts +1 -1
  40. package/dist/server/core-routes-plugin.js +2 -0
  41. package/dist/usage/actions/get-usage-metrics.js +4 -1
  42. package/dist/usage/metrics-store.d.ts +19 -0
  43. package/dist/usage/metrics-store.js +106 -14
  44. package/dist/usage/store.d.ts +7 -2
  45. package/dist/usage/store.js +25 -4
  46. package/package.json +3 -3
@@ -744,6 +744,14 @@ async function* parseJsonlStream(reader, model, captureContext = {}) {
744
744
  }
745
745
  case "usage": {
746
746
  const cacheWrite = (event.cacheCreatedTokens ?? 0) + (event.cacheCreated1hTokens ?? 0);
747
+ if (event.creditsUsed !== undefined &&
748
+ (!Number.isFinite(event.creditsUsed) || event.creditsUsed < 0)) {
749
+ yield gatewayErrorStop({
750
+ error: "Builder gateway returned invalid credit usage",
751
+ errorCode: "builder_gateway_error",
752
+ }, captureContext.creditsLane, captureContext.requestShape);
753
+ return;
754
+ }
747
755
  yield {
748
756
  type: "usage",
749
757
  inputTokens: event.inputTokens ?? 0,
@@ -752,6 +760,9 @@ async function* parseJsonlStream(reader, model, captureContext = {}) {
752
760
  ? { cacheReadTokens: event.cacheInputTokens }
753
761
  : {}),
754
762
  ...(cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}),
763
+ ...(event.creditsUsed !== undefined
764
+ ? { builderCreditsUsed: event.creditsUsed }
765
+ : {}),
755
766
  };
756
767
  break;
757
768
  }
@@ -196,6 +196,7 @@ export type EngineEvent = {
196
196
  cacheWriteTokens?: number;
197
197
  totalTokens?: number;
198
198
  reasoningTokens?: number;
199
+ builderCreditsUsed?: number;
199
200
  } | {
200
201
  /** Final assistant content for the turn. Engines MUST emit this
201
202
  * exactly once, immediately before the terminal `stop` event. */
@@ -571,6 +571,8 @@ export interface AgentLoopUsage {
571
571
  outputTokens: number;
572
572
  cacheReadTokens: number;
573
573
  cacheWriteTokens: number;
574
+ builderCreditsUsed?: number;
575
+ engineName?: string;
574
576
  model: string;
575
577
  /** Number of provider model-stream attempts, including retries. */
576
578
  llmCalls?: number;
@@ -3735,6 +3735,7 @@ export async function runAgentLoop(opts) {
3735
3735
  outputTokens: 0,
3736
3736
  cacheReadTokens: 0,
3737
3737
  cacheWriteTokens: 0,
3738
+ engineName: opts.engine.name,
3738
3739
  model,
3739
3740
  };
3740
3741
  const maxIterations = normalizeMaxIterations(opts.maxIterations, getDefaultMaxIterations());
@@ -4313,12 +4314,21 @@ export async function runAgentLoop(opts) {
4313
4314
  outputTokens: event.outputTokens,
4314
4315
  cacheReadTokens: event.cacheReadTokens ?? 0,
4315
4316
  cacheWriteTokens: event.cacheWriteTokens ?? 0,
4317
+ engineName: opts.engine.name,
4316
4318
  model,
4319
+ ...(event.builderCreditsUsed !== undefined
4320
+ ? { builderCreditsUsed: event.builderCreditsUsed }
4321
+ : {}),
4317
4322
  };
4318
4323
  usage.inputTokens += eventUsage.inputTokens;
4319
4324
  usage.outputTokens += eventUsage.outputTokens;
4320
4325
  usage.cacheReadTokens += eventUsage.cacheReadTokens;
4321
4326
  usage.cacheWriteTokens += eventUsage.cacheWriteTokens;
4327
+ if (eventUsage.builderCreditsUsed !== undefined) {
4328
+ usage.builderCreditsUsed =
4329
+ (usage.builderCreditsUsed ?? 0) +
4330
+ eventUsage.builderCreditsUsed;
4331
+ }
4322
4332
  usage.usageReported = true;
4323
4333
  opts.onUsage?.(eventUsage);
4324
4334
  }
@@ -6188,6 +6198,7 @@ export async function runAgentLoopWithMainChatInternalContinuations(opts) {
6188
6198
  outputTokens: 0,
6189
6199
  cacheReadTokens: 0,
6190
6200
  cacheWriteTokens: 0,
6201
+ engineName: opts.engine.name,
6191
6202
  model: opts.model,
6192
6203
  };
6193
6204
  const addUsage = (next) => {
@@ -6195,6 +6206,11 @@ export async function runAgentLoopWithMainChatInternalContinuations(opts) {
6195
6206
  usage.outputTokens += next.outputTokens;
6196
6207
  usage.cacheReadTokens += next.cacheReadTokens;
6197
6208
  usage.cacheWriteTokens += next.cacheWriteTokens;
6209
+ if (next.builderCreditsUsed !== undefined) {
6210
+ usage.builderCreditsUsed =
6211
+ (usage.builderCreditsUsed ?? 0) + next.builderCreditsUsed;
6212
+ }
6213
+ usage.engineName = next.engineName ?? usage.engineName;
6198
6214
  usage.model = next.model;
6199
6215
  if (typeof next.llmCalls === "number") {
6200
6216
  usage.llmCalls = (usage.llmCalls ?? 0) + next.llmCalls;
@@ -9074,6 +9090,8 @@ export function createProductionAgentHandler(options) {
9074
9090
  outputTokens: subUsage.outputTokens,
9075
9091
  cacheReadTokens: subUsage.cacheReadTokens,
9076
9092
  cacheWriteTokens: subUsage.cacheWriteTokens,
9093
+ builderCreditsUsed: subUsage.builderCreditsUsed,
9094
+ engineName: engine.name,
9077
9095
  model: subUsage.model,
9078
9096
  label: `custom-agent:${ref.name}`,
9079
9097
  runId,
@@ -9178,6 +9196,7 @@ export function createProductionAgentHandler(options) {
9178
9196
  outputTokens: 0,
9179
9197
  cacheReadTokens: 0,
9180
9198
  cacheWriteTokens: 0,
9199
+ engineName: engine.name,
9181
9200
  model: effectiveModel,
9182
9201
  };
9183
9202
  const agentLoopOpts = {
@@ -9197,6 +9216,11 @@ export function createProductionAgentHandler(options) {
9197
9216
  turnUsage.outputTokens += usage.outputTokens;
9198
9217
  turnUsage.cacheReadTokens += usage.cacheReadTokens;
9199
9218
  turnUsage.cacheWriteTokens += usage.cacheWriteTokens;
9219
+ if (usage.builderCreditsUsed !== undefined) {
9220
+ turnUsage.builderCreditsUsed =
9221
+ (turnUsage.builderCreditsUsed ?? 0) + usage.builderCreditsUsed;
9222
+ }
9223
+ turnUsage.engineName = usage.engineName ?? turnUsage.engineName;
9200
9224
  turnUsage.model = usage.model;
9201
9225
  },
9202
9226
  ownerEmail,
@@ -9367,7 +9391,8 @@ export function createProductionAgentHandler(options) {
9367
9391
  (turnUsage.inputTokens > 0 ||
9368
9392
  turnUsage.outputTokens > 0 ||
9369
9393
  turnUsage.cacheReadTokens > 0 ||
9370
- turnUsage.cacheWriteTokens > 0)) {
9394
+ turnUsage.cacheWriteTokens > 0 ||
9395
+ turnUsage.builderCreditsUsed != null)) {
9371
9396
  const { recordUsage } = await import("../usage/store.js");
9372
9397
  await recordUsage({
9373
9398
  ownerEmail: resolvedOwnerEmail,
@@ -9375,6 +9400,8 @@ export function createProductionAgentHandler(options) {
9375
9400
  outputTokens: turnUsage.outputTokens,
9376
9401
  cacheReadTokens: turnUsage.cacheReadTokens,
9377
9402
  cacheWriteTokens: turnUsage.cacheWriteTokens,
9403
+ builderCreditsUsed: turnUsage.builderCreditsUsed,
9404
+ engineName: engine.name,
9378
9405
  model: turnUsage.model,
9379
9406
  label: turnUsageLabel || "chat",
9380
9407
  // token_usage has had run_id/thread_id/task_id since it was
@@ -373,6 +373,7 @@ control) {
373
373
  outputTokens: 0,
374
374
  cacheReadTokens: 0,
375
375
  cacheWriteTokens: 0,
376
+ engineName: opts.engine.name,
376
377
  model: opts.model,
377
378
  };
378
379
  const addUsage = (next) => {
@@ -380,6 +381,11 @@ control) {
380
381
  usage.outputTokens += next.outputTokens;
381
382
  usage.cacheReadTokens += next.cacheReadTokens;
382
383
  usage.cacheWriteTokens += next.cacheWriteTokens;
384
+ if (next.builderCreditsUsed !== undefined) {
385
+ usage.builderCreditsUsed =
386
+ (usage.builderCreditsUsed ?? 0) + next.builderCreditsUsed;
387
+ }
388
+ usage.engineName = next.engineName ?? usage.engineName;
383
389
  usage.model = next.model;
384
390
  // Without these, a retry that never got a usage report merges its zeros
385
391
  // over an earlier attempt's real numbers, and telemetry reports an
@@ -186,8 +186,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
186
186
  duplicate?: undefined;
187
187
  eventId?: undefined;
188
188
  } | {
189
+ error?: undefined;
189
190
  accepted: boolean;
190
191
  duplicate: boolean;
191
192
  eventId: string;
192
- error?: undefined;
193
193
  }>;
@@ -21,7 +21,7 @@ export const BUILDER_SPACE_SETTINGS_URL = "https://builder.io/account/space?utm_
21
21
  export const NEW_CHAT_ACTION_HREF = "agent-native:new-chat";
22
22
  const OPEN_BUILDER_SPACE_SETTINGS_LABEL = "Open Builder space settings";
23
23
  const START_NEW_CHAT_LABEL = "Start new chat";
24
- const UPGRADE_AT_BUILDER_LABEL = "Upgrade at builder.io";
24
+ const ADD_CREDITS_IN_BUILDER_LABEL = "Add credits in Builder";
25
25
  const BUILDER_AUTHENTICATION_ERROR = "Builder rejected the connected credentials. Reconnect Builder.io (free tier available) in Settings, then retry.";
26
26
  /**
27
27
  * A 401 says the credential this request carried was refused. It does NOT say
@@ -100,7 +100,7 @@ export function formatChatErrorText(errorMessage, upgradeUrl, errorCode) {
100
100
  const normalized = normalizeChatError(errorMessage, errorCode);
101
101
  if (normalized.message === CREDITS_LIMIT_REACHED_MESSAGE) {
102
102
  return upgradeUrl && isSafeUpgradeUrl(upgradeUrl)
103
- ? `${normalized.message}\n\n[${UPGRADE_AT_BUILDER_LABEL}](${upgradeUrl})`
103
+ ? `${normalized.message}\n\n[${ADD_CREDITS_IN_BUILDER_LABEL}](${upgradeUrl})`
104
104
  : normalized.message;
105
105
  }
106
106
  if (!isServerChosenVisitorMessage(normalized.message) &&
@@ -118,7 +118,7 @@ export function formatChatErrorText(errorMessage, upgradeUrl, errorCode) {
118
118
  if (!upgradeUrl || !isSafeUpgradeUrl(upgradeUrl)) {
119
119
  return `Error: ${normalized.message}`;
120
120
  }
121
- return `Error: ${normalized.message}\n\n[${UPGRADE_AT_BUILDER_LABEL}](${upgradeUrl})`;
121
+ return `Error: ${normalized.message}\n\n[${ADD_CREDITS_IN_BUILDER_LABEL}](${upgradeUrl})`;
122
122
  }
123
123
  /**
124
124
  * True when the server already decided what this reader may be told.
@@ -229,7 +229,7 @@ const KNOWN_CHAT_ERROR_ACTION_KEYS = new Map([
229
229
  "agentChat.errorMessages.openBuilderSpaceSettings",
230
230
  ],
231
231
  ["Start new chat", "agentChat.errorMessages.startNewChat"],
232
- ["Upgrade at builder.io", "agentChat.errorMessages.upgradeAtBuilder"],
232
+ ["Add credits in Builder", "agentChat.errorMessages.addCreditsInBuilder"],
233
233
  ]);
234
234
  /** Localize only Core's own normalized error copy; preserve provider details. */
235
235
  export function localizeKnownChatErrorText(text, t) {
@@ -8,6 +8,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "
8
8
  import { IconArrowUpRight, IconBell, IconChartLine, IconChevronDown, IconLoader2, IconMail, IconPencil, IconPlus, IconRefresh, IconUsers, IconX, } from "@tabler/icons-react";
9
9
  import { useState } from "react";
10
10
  import { Link } from "react-router";
11
+ import { useT } from "../i18n.js";
11
12
  import { useActionMutation, useActionQuery } from "../use-action.js";
12
13
  const LOOKBACKS = [
13
14
  { value: 7, label: "7d" },
@@ -33,13 +34,53 @@ function compactNumber(value) {
33
34
  return `${(value / 1_000).toFixed(1)}K`;
34
35
  return Math.round(value).toLocaleString();
35
36
  }
36
- function formatCost(cents, billing) {
37
- if (billing.unit === "builder-credits") {
38
- const credits = (cents / 100) *
37
+ function usageAmount(cents, billing, builderCredits, estimatedBuilderCredits) {
38
+ if (billing.unit !== "usd") {
39
+ if (typeof builderCredits === "number" ||
40
+ typeof estimatedBuilderCredits === "number") {
41
+ return (builderCredits ?? 0) + (estimatedBuilderCredits ?? 0);
42
+ }
43
+ if (billing.unit === "mixed")
44
+ return 0;
45
+ return ((cents / 100) *
39
46
  (billing.hardCostMarginMultiplier ?? 1.25) *
40
- (billing.creditsPerUsd ?? 20);
41
- return `${credits.toLocaleString(undefined, { maximumFractionDigits: 1 })} credits`;
47
+ (billing.creditsPerUsd ?? 20));
42
48
  }
49
+ return cents;
50
+ }
51
+ function formatCost(t, cents, billing, builderCredits, estimatedBuilderCredits, otherCostCents, includeOtherUsd = true) {
52
+ if (billing.unit !== "usd") {
53
+ const parts = [];
54
+ const actual = builderCredits ?? 0;
55
+ const estimated = estimatedBuilderCredits ??
56
+ (typeof builderCredits === "number" || billing.unit === "mixed"
57
+ ? 0
58
+ : usageAmount(cents, billing));
59
+ if (actual > 0) {
60
+ parts.push(`${actual.toLocaleString(undefined, { maximumFractionDigits: 3 })} ${t("agentChat.usage.builderCredits", { defaultValue: "Builder credits" })}`);
61
+ }
62
+ if (estimated > 0) {
63
+ parts.push(t("agentChat.usage.estimatedBuilderCredits", {
64
+ defaultValue: "~{{amount}} estimated credits",
65
+ amount: estimated.toLocaleString(undefined, {
66
+ maximumFractionDigits: 3,
67
+ }),
68
+ }));
69
+ }
70
+ if (includeOtherUsd && otherCostCents && otherCostCents > 0) {
71
+ parts.push(t("agentChat.usage.otherUsdSpend", {
72
+ defaultValue: "{{amount}} other USD",
73
+ amount: formatUsdCost(otherCostCents),
74
+ }));
75
+ }
76
+ return (parts.join(" · ") ||
77
+ t("agentChat.usage.noBuilderCredits", {
78
+ defaultValue: "0 Builder credits",
79
+ }));
80
+ }
81
+ return formatUsdCost(cents);
82
+ }
83
+ function formatUsdCost(cents) {
43
84
  if (cents < 100)
44
85
  return `${cents.toFixed(2)}¢`;
45
86
  return `$${(cents / 100).toLocaleString(undefined, {
@@ -69,11 +110,7 @@ function Trend({ daily, billing, }) {
69
110
  if (daily.length === 0) {
70
111
  return (_jsx("div", { className: "flex h-32 items-center justify-center rounded-lg border border-dashed border-border/70 text-sm text-muted-foreground", children: "No usage recorded in this lookback." }));
71
112
  }
72
- const values = daily.map((day) => billing.unit === "builder-credits"
73
- ? (day.costCents / 100) *
74
- (billing.hardCostMarginMultiplier ?? 1.25) *
75
- (billing.creditsPerUsd ?? 20)
76
- : day.costCents);
113
+ const values = daily.map((day) => usageAmount(day.costCents, billing, day.builderCredits, day.estimatedBuilderCredits));
77
114
  const max = Math.max(...values, 0.01);
78
115
  const points = values.map((value, index) => {
79
116
  const x = daily.length === 1 ? 50 : (index / (daily.length - 1)) * 100;
@@ -90,17 +127,38 @@ function MetricCard({ label, value, detail, accent = false, }) {
90
127
  return (_jsxs("div", { className: "min-w-0 rounded-lg border border-border/70 bg-card px-4 py-4", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }), _jsx("div", { className: `mt-1 truncate text-xl font-semibold tracking-[-0.02em] tabular-nums ${accent ? "text-primary" : "text-foreground"}`, children: value }), _jsx("div", { className: "mt-1 truncate text-xs text-muted-foreground", children: detail })] }));
91
128
  }
92
129
  function DriverList({ title, rows, billing, }) {
93
- const max = Math.max(...rows.map((row) => row.costCents), 1);
94
- return (_jsxs("div", { children: [_jsxs("div", { className: "mb-3 flex items-center justify-between gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: title }), _jsx("span", { className: "text-[11px] uppercase tracking-[0.08em] text-muted-foreground", children: "Spend" })] }), rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "No usage recorded yet." })) : (_jsx("div", { className: "space-y-3", children: rows.slice(0, 5).map((row) => (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between gap-3 text-sm", children: [_jsx("span", { className: "min-w-0 truncate text-foreground", title: row.label, children: row.label }), _jsx("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: formatCost(row.costCents, billing) })] }), _jsx("div", { className: "mt-1.5 h-1 overflow-hidden rounded-full bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-primary/70", style: {
95
- width: `${Math.max(4, (row.costCents / max) * 100)}%`,
96
- } }) })] }, row.key))) }))] }));
130
+ const t = useT();
131
+ const max = Math.max(...rows.map((row) => usageAmount(row.costCents, billing, row.builderCredits, row.estimatedBuilderCredits)), 1);
132
+ const maxOtherUsd = Math.max(...rows.map((row) => row.otherCostCents ?? 0), 1);
133
+ return (_jsxs("div", { children: [_jsxs("div", { className: "mb-3 flex items-center justify-between gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: title }), _jsx("span", { className: "text-[11px] uppercase tracking-[0.08em] text-muted-foreground", children: billing.unit === "mixed"
134
+ ? t("agentChat.usage.driverCreditsAndUsd", {
135
+ defaultValue: "Builder credits / USD",
136
+ })
137
+ : billing.unit === "builder-credits"
138
+ ? t("agentChat.usage.builderCredits", {
139
+ defaultValue: "Builder credits",
140
+ })
141
+ : "Spend" })] }), rows.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "No usage recorded yet." })) : (_jsx("div", { className: "space-y-3", children: rows.slice(0, 5).map((row) => {
142
+ const builderAmount = usageAmount(row.costCents, billing, row.builderCredits, row.estimatedBuilderCredits);
143
+ const otherOnly = billing.unit === "mixed" && builderAmount === 0;
144
+ const amount = otherOnly
145
+ ? (row.otherCostCents ?? 0)
146
+ : builderAmount;
147
+ const scale = otherOnly ? maxOtherUsd : max;
148
+ return (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between gap-3 text-sm", children: [_jsx("span", { className: "min-w-0 truncate text-foreground", title: row.label, children: row.label }), _jsx("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: formatCost(t, row.costCents, billing, row.builderCredits, row.estimatedBuilderCredits, row.otherCostCents) })] }), _jsx("div", { className: "mt-1.5 h-1 overflow-hidden rounded-full bg-muted", children: amount > 0 ? (_jsx("div", { className: "h-full rounded-full bg-primary/70", style: {
149
+ width: `${Math.max(4, (amount / scale) * 100)}%`,
150
+ } })) : null })] }, row.key));
151
+ }) }))] }));
97
152
  }
98
153
  function UsageLoadingState() {
99
154
  return (_jsxs("div", { className: "space-y-5", role: "status", "aria-label": "Loading usage", children: [_jsx("div", { className: "grid gap-3 sm:grid-cols-3", children: ["w-24", "w-16", "w-20"].map((width) => (_jsxs("div", { className: "rounded-lg border border-border/70 p-4", children: [_jsx(Skeleton, { className: `h-3 ${width}` }), _jsx(Skeleton, { className: "mt-3 h-6 w-24" }), _jsx(Skeleton, { className: "mt-2 h-3 w-28" })] }, width))) }), _jsxs("div", { className: "rounded-lg border border-border/70 p-4", children: [_jsx(Skeleton, { className: "h-4 w-32" }), _jsx(Skeleton, { className: "mt-3 h-32 w-full" })] })] }));
100
155
  }
101
156
  function AlertEditor({ draft, onChange, onSave, onCancel, isPending, }) {
157
+ const t = useT();
102
158
  const channelCount = Number(draft.inApp) + Number(draft.email);
103
- return (_jsxs("div", { className: "mt-3 rounded-lg border border-border/70 bg-muted/20 p-3", children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-[1fr_1fr_1fr]", children: [_jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Unit" }), _jsxs(Select, { value: draft.unit, onValueChange: (value) => onChange({ unit: value }), children: [_jsx(SelectTrigger, { className: "h-9 bg-background text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "usd", children: "Dollars" }), _jsx(SelectItem, { value: "builder-credits", children: "Builder credits" }), _jsx(SelectItem, { value: "tokens", children: "Tokens" })] })] })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Threshold" }), _jsx(Input, { type: "number", min: "0.01", step: draft.unit === "tokens" ? "1" : "0.01", value: draft.limit, onChange: (event) => onChange({ limit: event.target.value }), className: "h-9 bg-background text-sm" })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Window" }), _jsxs(Select, { value: draft.period, onValueChange: (value) => onChange({ period: value }), children: [_jsx(SelectTrigger, { className: "h-9 bg-background text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "day", children: "Per day" }), _jsx(SelectItem, { value: "month", children: "Per month" })] })] })] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-4 text-xs text-muted-foreground", children: [_jsx("span", { className: "font-medium text-foreground", children: "Deliver via" }), _jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx(Checkbox, { checked: draft.inApp, onCheckedChange: (checked) => onChange({ inApp: checked === true }) }), _jsx(IconBell, { className: "size-3.5" }), " In app"] }), _jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx(Checkbox, { checked: draft.email, onCheckedChange: (checked) => onChange({ email: checked === true }) }), _jsx(IconMail, { className: "size-3.5" }), " Email"] }), _jsxs("div", { className: "ms-auto flex items-center gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancel, children: "Cancel" }), _jsxs(Button, { type: "button", size: "sm", onClick: onSave, disabled: isPending || channelCount === 0, children: [isPending ? _jsx(IconLoader2, { className: "animate-spin" }) : null, "Save alert"] })] })] })] }));
159
+ return (_jsxs("div", { className: "mt-3 rounded-lg border border-border/70 bg-muted/20 p-3", children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-[1fr_1fr_1fr]", children: [_jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Unit" }), _jsxs(Select, { value: draft.unit, onValueChange: (value) => onChange({ unit: value }), children: [_jsx(SelectTrigger, { className: "h-9 bg-background text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "usd", children: "Dollars" }), _jsx(SelectItem, { value: "builder-credits", children: t("agentChat.usage.builderCredits", {
160
+ defaultValue: "Builder credits",
161
+ }) }), _jsx(SelectItem, { value: "tokens", children: "Tokens" })] })] })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Threshold" }), _jsx(Input, { type: "number", min: "0.01", step: draft.unit === "tokens" ? "1" : "0.01", value: draft.limit, onChange: (event) => onChange({ limit: event.target.value }), className: "h-9 bg-background text-sm" })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Window" }), _jsxs(Select, { value: draft.period, onValueChange: (value) => onChange({ period: value }), children: [_jsx(SelectTrigger, { className: "h-9 bg-background text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "day", children: "Per day" }), _jsx(SelectItem, { value: "month", children: "Per month" })] })] })] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-4 text-xs text-muted-foreground", children: [_jsx("span", { className: "font-medium text-foreground", children: "Deliver via" }), _jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx(Checkbox, { checked: draft.inApp, onCheckedChange: (checked) => onChange({ inApp: checked === true }) }), _jsx(IconBell, { className: "size-3.5" }), " In app"] }), _jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx(Checkbox, { checked: draft.email, onCheckedChange: (checked) => onChange({ email: checked === true }) }), _jsx(IconMail, { className: "size-3.5" }), " Email"] }), _jsxs("div", { className: "ms-auto flex items-center gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancel, children: "Cancel" }), _jsxs(Button, { type: "button", size: "sm", onClick: onSave, disabled: isPending || channelCount === 0, children: [isPending ? _jsx(IconLoader2, { className: "animate-spin" }) : null, "Save alert"] })] })] })] }));
104
162
  }
105
163
  function UsageAlertsSection({ appId }) {
106
164
  const query = useActionQuery("get-usage-alerts", {
@@ -176,6 +234,7 @@ function UsageAlertsSection({ appId }) {
176
234
  }), editing && !editing.ruleId ? (_jsxs("div", { className: "px-4 py-4", children: [_jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: appLabel(editing.appId) }), _jsx(AlertEditor, { draft: editing, onChange: (patch) => setEditing((current) => current ? { ...current, ...patch } : current), onSave: saveDraft, onCancel: () => setEditing(null), isPending: mutation.isPending })] })) : null] })] }));
177
235
  }
178
236
  export function UsageSection({ appId = null, viewAllHref, }) {
237
+ const t = useT();
179
238
  const [sinceDays, setSinceDays] = useState(30);
180
239
  const [scope, setScope] = useState("me");
181
240
  const [selectedUserEmail, setSelectedUserEmail] = useState(null);
@@ -199,8 +258,37 @@ export function UsageSection({ appId = null, viewAllHref, }) {
199
258
  return (_jsxs("div", { className: "mx-auto w-full max-w-5xl space-y-6", children: [_jsxs("header", { className: "flex flex-col gap-4 border-b border-border/70 pb-5 lg:flex-row lg:items-end lg:justify-between", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium uppercase tracking-[0.1em] text-primary", children: [_jsx(IconChartLine, { className: "size-4" }), " Usage"] }), _jsxs("h1", { className: "mt-2 text-xl font-semibold tracking-[-0.02em] text-foreground", children: [appLabel(appId), " usage"] }), _jsx("p", { className: "mt-1 max-w-xl text-sm leading-6 text-muted-foreground", children: "A focused view of spend, capacity, and the prompts driving this app." })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [data?.access.canViewWorkspace ? (_jsxs(Select, { value: scope, onValueChange: (value) => {
200
259
  setScope(value);
201
260
  setSelectedUserEmail(null);
202
- }, children: [_jsx(SelectTrigger, { className: "h-9 w-32 text-xs", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "me", children: "My usage" }), _jsx(SelectItem, { value: "workspace", children: "Workspace" })] })] })) : null, scope === "workspace" && data ? (_jsxs(Select, { value: selectedUserEmail ?? "all", onValueChange: (value) => setSelectedUserEmail(value === "all" ? null : value), children: [_jsx(SelectTrigger, { className: "h-9 w-44 text-xs", children: _jsx(SelectValue, { placeholder: "All users" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All users" }), data.availableUsers.map((user) => (_jsx(SelectItem, { value: user.email, children: user.email }, user.email)))] })] })) : null, _jsx("div", { className: "flex items-center rounded-md border border-border/70 p-0.5", children: LOOKBACKS.map((range) => (_jsx(Button, { type: "button", variant: sinceDays === range.value ? "secondary" : "ghost", size: "sm", className: "h-8 px-2.5 text-xs", onClick: () => setSinceDays(range.value), children: range.label }, range.value))) }), _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => void query.refetch(), disabled: query.isFetching, "aria-label": "Refresh usage", title: "Refresh usage", children: query.isFetching ? (_jsx(IconLoader2, { className: "animate-spin" })) : (_jsx(IconRefresh, {})) })] })] }), query.isError && !data ? (_jsxs("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-4 text-sm", role: "alert", children: [_jsx("div", { className: "font-medium text-destructive", children: "Usage data couldn\u2019t be loaded." }), _jsx(Button, { type: "button", variant: "link", size: "sm", className: "mt-1 h-auto p-0", onClick: () => void query.refetch(), children: "Try again" })] })) : null, !data && query.isLoading ? _jsx(UsageLoadingState, {}) : null, data ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-3", children: [_jsx(MetricCard, { label: billing.label, value: formatCost(data.totals.costCents, billing), detail: `${sinceDays} day lookback`, accent: true }), _jsx(MetricCard, { label: "Calls", value: data.totals.calls.toLocaleString(), detail: `${data.currentDay.calls.toLocaleString()} today` }), _jsx(MetricCard, { label: "Tokens", value: compactNumber(totalTokens), detail: `${compactNumber(data.currentDay.tokens)} today` })] }), _jsxs("section", { className: "rounded-lg border border-border/70 bg-card p-4", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: "Daily trend" }), _jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [formatCost(data.currentDay.costCents, billing), " used today \u00B7", " ", data.currentDay.calls.toLocaleString(), " calls"] })] }), _jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [scope === "workspace" ? (_jsx(IconUsers, { className: "size-3.5" })) : null, scope === "workspace"
261
+ }, children: [_jsx(SelectTrigger, { className: "h-9 w-32 text-xs", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "me", children: "My usage" }), _jsx(SelectItem, { value: "workspace", children: "Workspace" })] })] })) : null, scope === "workspace" && data ? (_jsxs(Select, { value: selectedUserEmail ?? "all", onValueChange: (value) => setSelectedUserEmail(value === "all" ? null : value), children: [_jsx(SelectTrigger, { className: "h-9 w-44 text-xs", children: _jsx(SelectValue, { placeholder: "All users" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All users" }), data.availableUsers.map((user) => (_jsx(SelectItem, { value: user.email, children: user.email }, user.email)))] })] })) : null, _jsx("div", { className: "flex items-center rounded-md border border-border/70 p-0.5", children: LOOKBACKS.map((range) => (_jsx(Button, { type: "button", variant: sinceDays === range.value ? "secondary" : "ghost", size: "sm", className: "h-8 px-2.5 text-xs", onClick: () => setSinceDays(range.value), children: range.label }, range.value))) }), _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => void query.refetch(), disabled: query.isFetching, "aria-label": "Refresh usage", title: "Refresh usage", children: query.isFetching ? (_jsx(IconLoader2, { className: "animate-spin" })) : (_jsx(IconRefresh, {})) })] })] }), query.isError && !data ? (_jsxs("div", { className: "rounded-lg border border-destructive/30 bg-destructive/5 px-4 py-4 text-sm", role: "alert", children: [_jsx("div", { className: "font-medium text-destructive", children: "Usage data couldn\u2019t be loaded." }), _jsx(Button, { type: "button", variant: "link", size: "sm", className: "mt-1 h-auto p-0", onClick: () => void query.refetch(), children: "Try again" })] })) : null, !data && query.isLoading ? _jsx(UsageLoadingState, {}) : null, data ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: `grid gap-3 ${billing.unit === "mixed" ? "sm:grid-cols-2 xl:grid-cols-4" : "sm:grid-cols-3"}`, children: [_jsx(MetricCard, { label: billing.unit === "builder-credits"
262
+ ? t("agentChat.usage.builderCredits", {
263
+ defaultValue: "Builder credits",
264
+ })
265
+ : billing.unit === "mixed"
266
+ ? t("agentChat.usage.builderCredits", {
267
+ defaultValue: "Builder credits",
268
+ })
269
+ : billing.label, value: formatCost(t, data.totals.costCents, billing, data.totals.builderCredits, data.totals.estimatedBuilderCredits, data.totals.otherCostCents, false), detail: `${sinceDays} day lookback`, accent: true }), billing.unit === "mixed" ? (_jsx(MetricCard, { label: t("agentChat.usage.otherUnclassifiedSpend", {
270
+ defaultValue: "Other or unclassified USD spend",
271
+ }), value: formatUsdCost(data.totals.otherCostCents ?? 0), detail: t("agentChat.usage.providerSpendDetail", {
272
+ defaultValue: "Provider or older calls outside Builder billing",
273
+ }) })) : null, _jsx(MetricCard, { label: "Calls", value: data.totals.calls.toLocaleString(), detail: `${data.currentDay.calls.toLocaleString()} today` }), _jsx(MetricCard, { label: "Tokens", value: compactNumber(totalTokens), detail: `${compactNumber(data.currentDay.tokens)} today` })] }), _jsxs("section", { className: "rounded-lg border border-border/70 bg-card p-4", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: "Daily trend" }), _jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [formatCost(t, data.currentDay.costCents, billing, data.currentDay.credits, data.currentDay.estimatedBuilderCredits, data.currentDay.otherCostCents, false), " ", "used today \u00B7 ", data.currentDay.calls.toLocaleString(), " calls"] }), billing.unit === "mixed" &&
274
+ (data.currentDay.otherCalls ?? 0) > 0 ? (_jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t("agentChat.usage.providerSpendToday", {
275
+ defaultValue: "Other or unclassified usage: {{amount}} today",
276
+ amount: formatUsdCost(data.currentDay.otherCostCents ?? 0),
277
+ }) })) : null] }), _jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [scope === "workspace" ? (_jsx(IconUsers, { className: "size-3.5" })) : null, scope === "workspace"
203
278
  ? (selectedUserEmail ?? "All users")
204
- : "Your usage"] })] }), _jsx("div", { className: "mt-4", children: _jsx(Trend, { daily: data.daily, billing: billing }) })] }), _jsxs("details", { className: "group rounded-lg border border-border/70 bg-card", children: [_jsxs("summary", { className: "flex cursor-pointer list-none items-center justify-between gap-3 px-4 py-4 outline-none transition-colors hover:bg-accent/30 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset [&::-webkit-details-marker]:hidden", children: [_jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold text-foreground", children: "Top usage drivers" }), _jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: "Breakdowns stay collapsed until you need them." })] }), _jsx(IconChevronDown, { className: "size-4 text-muted-foreground transition-transform group-open:rotate-180" })] }), _jsxs("div", { className: "grid gap-6 border-t border-border/70 px-4 py-4 md:grid-cols-2", children: [_jsx(DriverList, { title: "By workflow", rows: data.byLabel, billing: billing }), _jsx(DriverList, { title: "By model", rows: data.byModel, billing: billing })] })] }), _jsxs("details", { className: "group rounded-lg border border-border/70 bg-card", children: [_jsxs("summary", { className: "flex cursor-pointer list-none items-center justify-between gap-3 px-4 py-4 outline-none transition-colors hover:bg-accent/30 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset [&::-webkit-details-marker]:hidden", children: [_jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold text-foreground", children: "Recent prompts" }), _jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: "Latest recorded prompts for the selected app and user scope." })] }), _jsxs("span", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [data.recent.length, _jsx(IconChevronDown, { className: "size-4 transition-transform group-open:rotate-180" })] })] }), _jsx("div", { className: "border-t border-border/70", children: data.recent.length === 0 ? (_jsx("p", { className: "px-4 py-5 text-sm text-muted-foreground", children: "No recent prompts recorded." })) : (_jsx("div", { className: "divide-y divide-border/60", children: data.recent.map((entry) => (_jsxs("div", { className: "px-4 py-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 text-[11px] text-muted-foreground", children: [_jsxs("span", { children: [entry.label, " \u00B7 ", entry.model] }), _jsx("span", { children: entry.ownerEmail })] }), _jsx("p", { className: "mt-1 line-clamp-2 text-sm text-foreground", children: entry.prompt ??
279
+ : "Your usage"] })] }), _jsx("div", { className: "mt-4", children: _jsxs("div", { className: billing.unit === "mixed" &&
280
+ (data.totals.otherCostCents ?? 0) > 0
281
+ ? "grid gap-4 md:grid-cols-2"
282
+ : "", children: [_jsxs("div", { children: [billing.unit === "mixed" ? (_jsx("h3", { className: "mb-2 text-xs font-medium text-muted-foreground", children: t("agentChat.usage.builderCredits", {
283
+ defaultValue: "Builder credits",
284
+ }) })) : null, _jsx(Trend, { daily: data.daily, billing: billing })] }), billing.unit === "mixed" &&
285
+ (data.totals.otherCostCents ?? 0) > 0 ? (_jsxs("div", { children: [_jsx("h3", { className: "mb-2 text-xs font-medium text-muted-foreground", children: t("agentChat.usage.otherUnclassifiedSpend", {
286
+ defaultValue: "Other or unclassified USD spend",
287
+ }) }), _jsx(Trend, { daily: data.daily.map((day) => ({
288
+ date: day.date,
289
+ costCents: day.otherCostCents ?? 0,
290
+ calls: day.otherCalls ?? 0,
291
+ tokens: 0,
292
+ })), billing: { unit: "usd", label: "USD" } })] })) : null] }) })] }), _jsxs("details", { className: "group rounded-lg border border-border/70 bg-card", children: [_jsxs("summary", { className: "flex cursor-pointer list-none items-center justify-between gap-3 px-4 py-4 outline-none transition-colors hover:bg-accent/30 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset [&::-webkit-details-marker]:hidden", children: [_jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold text-foreground", children: "Top usage drivers" }), _jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: "Breakdowns stay collapsed until you need them." })] }), _jsx(IconChevronDown, { className: "size-4 text-muted-foreground transition-transform group-open:rotate-180" })] }), _jsxs("div", { className: "grid gap-6 border-t border-border/70 px-4 py-4 md:grid-cols-2", children: [_jsx(DriverList, { title: "By workflow", rows: data.byLabel, billing: billing }), _jsx(DriverList, { title: "By model", rows: data.byModel, billing: billing })] })] }), _jsxs("details", { className: "group rounded-lg border border-border/70 bg-card", children: [_jsxs("summary", { className: "flex cursor-pointer list-none items-center justify-between gap-3 px-4 py-4 outline-none transition-colors hover:bg-accent/30 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset [&::-webkit-details-marker]:hidden", children: [_jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold text-foreground", children: "Recent prompts" }), _jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: "Latest recorded prompts for the selected app and user scope." })] }), _jsxs("span", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [data.recent.length, _jsx(IconChevronDown, { className: "size-4 transition-transform group-open:rotate-180" })] })] }), _jsx("div", { className: "border-t border-border/70", children: data.recent.length === 0 ? (_jsx("p", { className: "px-4 py-5 text-sm text-muted-foreground", children: "No recent prompts recorded." })) : (_jsx("div", { className: "divide-y divide-border/60", children: data.recent.map((entry) => (_jsxs("div", { className: "px-4 py-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 text-[11px] text-muted-foreground", children: [_jsxs("span", { children: [entry.label, " \u00B7 ", entry.model] }), _jsx("span", { children: entry.ownerEmail })] }), _jsx("p", { className: "mt-1 line-clamp-2 text-sm text-foreground", children: entry.prompt ??
205
293
  "Prompt text was not captured for this call." })] }, entry.id))) })) })] }), _jsx(UsageAlertsSection, { appId: appId }), viewAllHref ? (_jsx("div", { className: "flex justify-end", children: _jsx(Button, { asChild: true, variant: "link", size: "sm", className: "gap-1.5", children: _jsxs(Link, { to: viewAllHref, children: ["View all usage", _jsx(IconArrowUpRight, {})] }) }) })) : null] })) : null] }));
206
294
  }
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- error: string;
30
29
  ok?: undefined;
30
+ error: string;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -1,2 +1,2 @@
1
- export { CONNECT_APPS_FLAG, defineFeatureFlag, defineFeatureFlags, getFeatureFlagDefinition, listFeatureFlags, registerFeatureFlags, type FeatureFlagDefinition, } from "./registry.js";
1
+ export { BUILDER_CREDIT_USAGE_REPORTING_FLAG, CONNECT_APPS_FLAG, defineFeatureFlag, defineFeatureFlags, getFeatureFlagDefinition, listFeatureFlags, registerFeatureFlags, type FeatureFlagDefinition, } from "./registry.js";
2
2
  export { defaultFeatureFlagRules, evaluateFeatureFlag, evaluateFeatureFlagRules, hasActiveFeatureFlagRollout, isFeatureFlagEnabled, getFeatureFlagRules, normalizeFeatureFlagRules, type FeatureFlagMode, type FeatureFlagRules, type FeatureFlagScope, } from "./store.js";
@@ -1,4 +1,4 @@
1
- export { CONNECT_APPS_FLAG, defineFeatureFlag, defineFeatureFlags, getFeatureFlagDefinition, listFeatureFlags, registerFeatureFlags, } from "./registry.js";
1
+ export { BUILDER_CREDIT_USAGE_REPORTING_FLAG, CONNECT_APPS_FLAG, defineFeatureFlag, defineFeatureFlags, getFeatureFlagDefinition, listFeatureFlags, registerFeatureFlags, } from "./registry.js";
2
2
  export { defaultFeatureFlagRules, evaluateFeatureFlag, evaluateFeatureFlagRules, hasActiveFeatureFlagRollout, isFeatureFlagEnabled, getFeatureFlagRules, normalizeFeatureFlagRules, } from "./store.js";
3
3
  // Plugin and A2A auth stay on `./server` and `@agent-native/core/server`.
4
4
  // Re-exporting them here pulls HMAC Node builtins into any Vite client that
@@ -10,6 +10,7 @@ export interface FeatureFlagDefinition {
10
10
  export declare function defineFeatureFlag(definition: FeatureFlagDefinition): FeatureFlagDefinition;
11
11
  /** Framework-owned labs flag available to every app's feature-flag plugin. */
12
12
  export declare const CONNECT_APPS_FLAG: FeatureFlagDefinition;
13
+ export declare const BUILDER_CREDIT_USAGE_REPORTING_FLAG: FeatureFlagDefinition;
13
14
  /** Define a small app-owned feature-flag registry. */
14
15
  export declare function defineFeatureFlags(definitions: readonly FeatureFlagDefinition[]): readonly FeatureFlagDefinition[];
15
16
  /** Register definitions once at Nitro startup. Re-registering identical data is safe for HMR. */
@@ -25,6 +25,11 @@ export const CONNECT_APPS_FLAG = defineFeatureFlag({
25
25
  displayName: "Connect apps",
26
26
  description: "Show the experimental app connection surface.",
27
27
  });
28
+ export const BUILDER_CREDIT_USAGE_REPORTING_FLAG = defineFeatureFlag({
29
+ key: "billing.builder-credit-usage-reporting",
30
+ displayName: "Builder credit usage reporting",
31
+ description: "Use the Builder gateway's reported credit charges in usage reports.",
32
+ });
28
33
  /** Define a small app-owned feature-flag registry. */
29
34
  export function defineFeatureFlags(definitions) {
30
35
  const seen = new Set();
@@ -44,9 +44,9 @@ declare const _default: import("../../action.js").ActionDefinition<{
44
44
  url: string;
45
45
  id: string;
46
46
  provider: string;
47
- error?: undefined;
48
47
  configured?: undefined;
49
48
  connectPath?: undefined;
49
+ error?: undefined;
50
50
  } | {
51
51
  url?: undefined;
52
52
  id?: undefined;
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { createDevScriptRegistry } from "./scripts/dev/index.js";
11
11
  export { createAgentChatPlugin, defaultAgentChatPlugin, createAgentNativeEmbeddedPlugin, mountAgentNativeEmbedded, createServer, createSSEHandler, defineNitroPlugin, autoMountAuth, getSession, type AgentChatPluginOptions, type AgentNativeEmbeddedPluginOptions, type AuthSession, type AuthOptions, type CreateServerOptions, type SSEHandlerOptions, } from "./root-server-compat.js";
12
12
  export { BUILT_IN_INTEGRATION_CATALOG, INTEGRATION_CATEGORIES, getIntegrationCatalogEntry, listBuiltInChannelIntegrations, listIntegrationCatalog, type AutomationCapabilities, type BuiltInChannelId, type ChannelCapabilities, type IntegrationAvailability, type IntegrationCatalogEntry, type IntegrationCategory, type IntegrationCredentialRequirement, type IntegrationIconKey, type IntegrationSupportMaturity, } from "./integrations/catalog.js";
13
13
  export { AutomationConnectorError, createAutomationCallbackHandler, createAutomationRuntime, createInvokeAutomationWorkflowAction, type AutomationCallbackAuthentication, type AutomationCallbackInput, type AutomationCallbackResult, type AutomationInboundDefinition, type AutomationInvocation, type AutomationInvocationResult, type AutomationInvocationStatus, type AutomationOutboundDefinition, type AutomationResponseMode, type AutomationRetryPolicy, type AutomationRuntime, type AutomationRuntimeOptions, type AutomationWorkflowCapabilities, type AutomationWorkflowDefinition, type JsonSchema, } from "./automation/index.js";
14
- export { CONNECT_APPS_FLAG, defineFeatureFlag, defineFeatureFlags, evaluateFeatureFlag, getFeatureFlagDefinition, getFeatureFlagRules, hasActiveFeatureFlagRollout, isFeatureFlagEnabled, listFeatureFlags, registerFeatureFlags, type FeatureFlagDefinition, type FeatureFlagMode, type FeatureFlagRules, type FeatureFlagScope, } from "./feature-flags/index.js";
14
+ export { BUILDER_CREDIT_USAGE_REPORTING_FLAG, CONNECT_APPS_FLAG, defineFeatureFlag, defineFeatureFlags, evaluateFeatureFlag, getFeatureFlagDefinition, getFeatureFlagRules, hasActiveFeatureFlagRollout, isFeatureFlagEnabled, listFeatureFlags, registerFeatureFlags, type FeatureFlagDefinition, type FeatureFlagMode, type FeatureFlagRules, type FeatureFlagScope, } from "./feature-flags/index.js";
15
15
  export { createFeatureFlagA2AActionRouteAuth, createFeatureFlagsPlugin, } from "./feature-flags/server.js";
16
16
  export { defineLab, defineLabs, getLabDefinition, listLabs, registerLabs, type LabDefinition, } from "./labs/index.js";
17
17
  export { createLabsPlugin } from "./labs/server.js";
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ export { createDevScriptRegistry } from "./scripts/dev/index.js";
16
16
  export { createAgentChatPlugin, defaultAgentChatPlugin, createAgentNativeEmbeddedPlugin, mountAgentNativeEmbedded, createServer, createSSEHandler, defineNitroPlugin, autoMountAuth, getSession, } from "./root-server-compat.js";
17
17
  export { BUILT_IN_INTEGRATION_CATALOG, INTEGRATION_CATEGORIES, getIntegrationCatalogEntry, listBuiltInChannelIntegrations, listIntegrationCatalog, } from "./integrations/catalog.js";
18
18
  export { AutomationConnectorError, createAutomationCallbackHandler, createAutomationRuntime, createInvokeAutomationWorkflowAction, } from "./automation/index.js";
19
- export { CONNECT_APPS_FLAG, defineFeatureFlag, defineFeatureFlags, evaluateFeatureFlag, getFeatureFlagDefinition, getFeatureFlagRules, hasActiveFeatureFlagRollout, isFeatureFlagEnabled, listFeatureFlags, registerFeatureFlags, } from "./feature-flags/index.js";
19
+ export { BUILDER_CREDIT_USAGE_REPORTING_FLAG, CONNECT_APPS_FLAG, defineFeatureFlag, defineFeatureFlags, evaluateFeatureFlag, getFeatureFlagDefinition, getFeatureFlagRules, hasActiveFeatureFlagRollout, isFeatureFlagEnabled, listFeatureFlags, registerFeatureFlags, } from "./feature-flags/index.js";
20
20
  export { createFeatureFlagA2AActionRouteAuth, createFeatureFlagsPlugin, } from "./feature-flags/server.js";
21
21
  export { defineLab, defineLabs, getLabDefinition, listLabs, registerLabs, } from "./labs/index.js";
22
22
  export { createLabsPlugin } from "./labs/server.js";
@@ -1448,7 +1448,8 @@ async function recordIntegrationUsage(options) {
1448
1448
  (usage.inputTokens <= 0 &&
1449
1449
  usage.outputTokens <= 0 &&
1450
1450
  usage.cacheReadTokens <= 0 &&
1451
- usage.cacheWriteTokens <= 0)) {
1451
+ usage.cacheWriteTokens <= 0 &&
1452
+ usage.builderCreditsUsed == null)) {
1452
1453
  return;
1453
1454
  }
1454
1455
  try {
@@ -1459,6 +1460,8 @@ async function recordIntegrationUsage(options) {
1459
1460
  outputTokens: usage.outputTokens,
1460
1461
  cacheReadTokens: usage.cacheReadTokens,
1461
1462
  cacheWriteTokens: usage.cacheWriteTokens,
1463
+ builderCreditsUsed: usage.builderCreditsUsed,
1464
+ engineName: usage.engineName,
1462
1465
  model: usage.model,
1463
1466
  label: `integration:${options.incoming.platform}`,
1464
1467
  app: options.appId,
@@ -598,7 +598,8 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
598
598
  (usage.inputTokens > 0 ||
599
599
  usage.outputTokens > 0 ||
600
600
  usage.cacheReadTokens > 0 ||
601
- usage.cacheWriteTokens > 0)) {
601
+ usage.cacheWriteTokens > 0 ||
602
+ usage.builderCreditsUsed != null)) {
602
603
  try {
603
604
  const { recordUsage } = await import("../usage/store.js");
604
605
  await recordUsage({
@@ -607,6 +608,10 @@ async function executeBackgroundAutomation(options, deps, historyId, runIdRef) {
607
608
  outputTokens: usage.outputTokens,
608
609
  cacheReadTokens: usage.cacheReadTokens,
609
610
  cacheWriteTokens: usage.cacheWriteTokens,
611
+ ...(usage.builderCreditsUsed == null
612
+ ? {}
613
+ : { builderCreditsUsed: usage.builderCreditsUsed }),
614
+ engineName: usage.engineName ?? engine.name,
610
615
  model: usage.model,
611
616
  label: usageLabel,
612
617
  app: deps.appId,
@@ -425,7 +425,7 @@ const messages = {
425
425
  "errorMessages.providerRateLimit": "يفرض مزوّد النموذج حاليًا حدًا على معدل طلبات هذه المحادثة. انتظر قليلًا، ثم أعد المحاولة.",
426
426
  "errorMessages.providerTransientRejection": "رفض مزوّد الذكاء الاصطناعي هذا الطلب مؤقتًا. عادةً ما يُحل هذا خلال دقيقة — أعد المحاولة.",
427
427
  "errorMessages.startNewChat": "بدء محادثة جديدة",
428
- "errorMessages.upgradeAtBuilder": "الترقية عبر Builder.io",
428
+ "errorMessages.addCreditsInBuilder": "إضافة أرصدة في Builder",
429
429
  "feedback.inaccurate": "غير دقيق",
430
430
  "feedback.keyboardHint": "{{shortcut}} Enter للإرسال",
431
431
  "feedback.notHelpful": "غير مفيد",
@@ -753,5 +753,13 @@ const messages = {
753
753
  "settings.emailChangeError": "تعذر إرسال التأكيد.",
754
754
  "settings.emailNewLabel": "البريد الإلكتروني الجديد",
755
755
  "settings.emailNewPlaceholder": "أدخل بريدًا إلكترونيًا جديدًا",
756
+ "usage.builderCredits": "أرصدة Builder",
757
+ "usage.estimatedBuilderCredits": "~{{amount}} رصيد مقدّر",
758
+ "usage.otherUsdSpend": "{{amount}} دولار أمريكي أخرى",
759
+ "usage.noBuilderCredits": "0 من أرصدة Builder",
760
+ "usage.otherUnclassifiedSpend": "إنفاق آخر أو غير مصنّف بالدولار الأمريكي",
761
+ "usage.providerSpendDetail": "استخدام المزوّد أو المكالمات الأقدم خارج فوترة Builder",
762
+ "usage.providerSpendToday": "استخدام آخر أو غير مصنّف اليوم: {{amount}}",
763
+ "usage.driverCreditsAndUsd": "أرصدة Builder / دولار أمريكي",
756
764
  };
757
765
  export default messages;
@@ -595,7 +595,7 @@ const messages = {
595
595
  "errorMessages.providerRateLimit": "Der Modellanbieter begrenzt diesen Chat derzeit. Warte einen Moment und versuche es dann erneut.",
596
596
  "errorMessages.providerTransientRejection": "Der KI-Anbieter hat diese Anfrage vorübergehend abgelehnt. Das behebt sich meist innerhalb einer Minute – versuche es erneut.",
597
597
  "errorMessages.startNewChat": "Neuen Chat starten",
598
- "errorMessages.upgradeAtBuilder": "Bei Builder.io upgraden",
598
+ "errorMessages.addCreditsInBuilder": "Credits bei Builder hinzufügen",
599
599
  "feedback.inaccurate": "Ungenau",
600
600
  "feedback.keyboardHint": "{{shortcut}} Enter zum Senden",
601
601
  "feedback.notHelpful": "Nicht hilfreich",
@@ -725,5 +725,13 @@ const messages = {
725
725
  "settings.emailChangeError": "Bestätigung konnte nicht gesendet werden.",
726
726
  "settings.emailNewLabel": "Neue E-Mail-Adresse",
727
727
  "settings.emailNewPlaceholder": "Neue E-Mail-Adresse eingeben",
728
+ "usage.builderCredits": "Builder-Credits",
729
+ "usage.estimatedBuilderCredits": "~{{amount}} geschätzte Credits",
730
+ "usage.otherUsdSpend": "{{amount}} USD sonstige",
731
+ "usage.noBuilderCredits": "0 Builder-Credits",
732
+ "usage.otherUnclassifiedSpend": "Sonstige oder nicht zugeordnete Ausgaben in USD",
733
+ "usage.providerSpendDetail": "Anbieteraufrufe oder ältere Aufrufe außerhalb der Builder-Abrechnung",
734
+ "usage.providerSpendToday": "Sonstige oder nicht zugeordnete Nutzung heute: {{amount}}",
735
+ "usage.driverCreditsAndUsd": "Builder-Credits / USD",
728
736
  };
729
737
  export default messages;