@agent-native/core 0.94.3 → 0.95.1

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 (181) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +16 -0
  3. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +13 -0
  4. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +18 -0
  5. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +16 -0
  6. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +17 -0
  7. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +16 -0
  8. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +17 -0
  9. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +15 -0
  10. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +16 -0
  11. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +11 -0
  12. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +12 -0
  13. package/corpus/core/docs/content/pr-visual-recap.mdx +20 -12
  14. package/corpus/core/docs/content/template-design.mdx +16 -0
  15. package/corpus/core/package.json +2 -2
  16. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +3 -0
  17. package/corpus/core/src/agent/engine/registry.ts +6 -1
  18. package/corpus/core/src/agent/engine/types.ts +2 -0
  19. package/corpus/core/src/agent/production-agent.ts +20 -0
  20. package/corpus/core/src/agent/thread-data-builder.ts +41 -17
  21. package/corpus/core/src/cli/code-agent-executor.ts +61 -1
  22. package/corpus/core/src/cli/pr-visual-recap-workflow.ts +1 -1
  23. package/corpus/core/src/cli/recap.ts +262 -25
  24. package/corpus/core/src/client/AssistantChat.tsx +250 -25
  25. package/corpus/core/src/client/chat/tool-call-display.tsx +1 -1
  26. package/corpus/core/src/client/chat/use-reconnect-reader-owner.ts +28 -0
  27. package/corpus/core/src/client/guided-questions.tsx +9 -1
  28. package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +6 -7
  29. package/corpus/core/src/client/settings/UsageSection.tsx +26 -5
  30. package/corpus/core/src/client/sse-event-processor.ts +90 -21
  31. package/corpus/core/src/connections/catalog.ts +2 -2
  32. package/corpus/core/src/deploy/build.ts +3 -0
  33. package/corpus/core/src/index.ts +2 -0
  34. package/corpus/core/src/integrations/catalog.ts +2 -2
  35. package/corpus/core/src/server/auth.ts +1 -1
  36. package/corpus/core/src/server/credential-provider.ts +7 -3
  37. package/corpus/core/src/shared/cache-control.ts +10 -4
  38. package/corpus/core/src/usage/store.ts +82 -20
  39. package/corpus/templates/analytics/app/lib/data-sources.ts +5 -4
  40. package/corpus/templates/analytics/changelog/2026-07-10-slack-token-setup-marked-legacy.md +6 -0
  41. package/corpus/templates/analytics/server/lib/credential-keys.ts +6 -2
  42. package/corpus/templates/brain/changelog/2026-07-10-slack-backfill-token-marked-legacy.md +6 -0
  43. package/corpus/templates/brain/server/register-secrets.ts +2 -2
  44. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -1
  45. package/corpus/templates/clips/app/components/library/page-header.tsx +19 -3
  46. package/corpus/templates/clips/app/routes/_app.library._index.tsx +16 -12
  47. package/corpus/templates/clips/changelog/2026-07-10-new-recording-appears-once-when-the-library-sidebar-is-open.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-07-10-slack-token-fallback-guidance.md +6 -0
  49. package/corpus/templates/clips/server/register-secrets.ts +1 -1
  50. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +0 -66
  51. package/corpus/templates/content/app/routes/_app.local-files.tsx +7 -47
  52. package/corpus/templates/content/changelog/2026-07-10-local-folders-now-wait-for-an-explicit-pull-before-content-r.md +6 -0
  53. package/corpus/templates/content/package.json +1 -1
  54. package/corpus/templates/design/actions/generate-design.ts +128 -31
  55. package/corpus/templates/design/actions/present-design-variants.ts +37 -9
  56. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +9 -3
  57. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +32 -10
  58. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +2 -2
  59. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +9 -1
  60. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -5
  61. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  62. package/corpus/templates/design/app/global.css +10 -0
  63. package/corpus/templates/design/app/hooks/use-question-flow.ts +9 -2
  64. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  65. package/corpus/templates/design/app/i18n-data.ts +21 -11
  66. package/corpus/templates/design/changelog/2026-07-10-editor-panels-are-easier-to-distinguish-and-design-intake-qu.md +6 -0
  67. package/corpus/templates/design/changelog/2026-07-10-interact-mode-now-stays-on-the-full-canvas-matches-edit-visu.md +6 -0
  68. package/corpus/templates/design/changelog/2026-07-10-new-web-designs-open-at-desktop-size-and-adapt-across-mobile.md +6 -0
  69. package/corpus/templates/design/changelog/2026-07-10-tweaks-now-link-to-full-documentation.md +6 -0
  70. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  71. package/corpus/templates/mail/app/i18n/ar-SA.ts +3 -3
  72. package/corpus/templates/mail/app/i18n/de-DE.ts +3 -3
  73. package/corpus/templates/mail/app/i18n/en-US.ts +4 -3
  74. package/corpus/templates/mail/app/i18n/es-ES.ts +3 -3
  75. package/corpus/templates/mail/app/i18n/fr-FR.ts +3 -3
  76. package/corpus/templates/mail/app/i18n/hi-IN.ts +3 -3
  77. package/corpus/templates/mail/app/i18n/ja-JP.ts +3 -3
  78. package/corpus/templates/mail/app/i18n/ko-KR.ts +3 -3
  79. package/corpus/templates/mail/app/i18n/pt-BR.ts +3 -3
  80. package/corpus/templates/mail/app/i18n/zh-CN.ts +3 -3
  81. package/corpus/templates/mail/app/i18n/zh-TW.ts +3 -3
  82. package/corpus/templates/mail/changelog/2026-07-10-slack-intake-marked-legacy.md +6 -0
  83. package/corpus/templates/mail/server/lib/env-config.ts +5 -4
  84. package/corpus/templates/plan/actions/record-recap-usage-cost.ts +49 -0
  85. package/corpus/templates/plan/actions/record-recap-usage.ts +15 -22
  86. package/corpus/templates/plan/package.json +1 -1
  87. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  88. package/dist/agent/engine/ai-sdk-engine.js +3 -0
  89. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  90. package/dist/agent/engine/registry.d.ts +1 -1
  91. package/dist/agent/engine/registry.d.ts.map +1 -1
  92. package/dist/agent/engine/registry.js +2 -0
  93. package/dist/agent/engine/registry.js.map +1 -1
  94. package/dist/agent/engine/types.d.ts +2 -0
  95. package/dist/agent/engine/types.d.ts.map +1 -1
  96. package/dist/agent/engine/types.js.map +1 -1
  97. package/dist/agent/production-agent.d.ts.map +1 -1
  98. package/dist/agent/production-agent.js +20 -0
  99. package/dist/agent/production-agent.js.map +1 -1
  100. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  101. package/dist/agent/thread-data-builder.js +36 -18
  102. package/dist/agent/thread-data-builder.js.map +1 -1
  103. package/dist/cli/code-agent-executor.d.ts +2 -0
  104. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  105. package/dist/cli/code-agent-executor.js +52 -3
  106. package/dist/cli/code-agent-executor.js.map +1 -1
  107. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  108. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  109. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  110. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  111. package/dist/cli/recap.d.ts +22 -2
  112. package/dist/cli/recap.d.ts.map +1 -1
  113. package/dist/cli/recap.js +199 -22
  114. package/dist/cli/recap.js.map +1 -1
  115. package/dist/client/AssistantChat.d.ts +3 -1
  116. package/dist/client/AssistantChat.d.ts.map +1 -1
  117. package/dist/client/AssistantChat.js +200 -28
  118. package/dist/client/AssistantChat.js.map +1 -1
  119. package/dist/client/chat/tool-call-display.js +1 -1
  120. package/dist/client/chat/tool-call-display.js.map +1 -1
  121. package/dist/client/chat/use-reconnect-reader-owner.d.ts +11 -0
  122. package/dist/client/chat/use-reconnect-reader-owner.d.ts.map +1 -0
  123. package/dist/client/chat/use-reconnect-reader-owner.js +21 -0
  124. package/dist/client/chat/use-reconnect-reader-owner.js.map +1 -0
  125. package/dist/client/guided-questions.d.ts.map +1 -1
  126. package/dist/client/guided-questions.js +5 -1
  127. package/dist/client/guided-questions.js.map +1 -1
  128. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  129. package/dist/client/integrations/IntegrationsPanel.js +5 -7
  130. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  131. package/dist/client/settings/UsageSection.d.ts.map +1 -1
  132. package/dist/client/settings/UsageSection.js +12 -3
  133. package/dist/client/settings/UsageSection.js.map +1 -1
  134. package/dist/client/sse-event-processor.d.ts.map +1 -1
  135. package/dist/client/sse-event-processor.js +67 -20
  136. package/dist/client/sse-event-processor.js.map +1 -1
  137. package/dist/collab/routes.d.ts +1 -1
  138. package/dist/collab/struct-routes.d.ts +1 -1
  139. package/dist/connections/catalog.d.ts +2 -2
  140. package/dist/connections/catalog.js +2 -2
  141. package/dist/connections/catalog.js.map +1 -1
  142. package/dist/deploy/build.d.ts.map +1 -1
  143. package/dist/deploy/build.js +3 -1
  144. package/dist/deploy/build.js.map +1 -1
  145. package/dist/index.d.ts +1 -1
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js.map +1 -1
  148. package/dist/integrations/catalog.js +2 -2
  149. package/dist/integrations/catalog.js.map +1 -1
  150. package/dist/notifications/routes.d.ts +2 -2
  151. package/dist/observability/routes.d.ts +1 -1
  152. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  153. package/dist/resources/handlers.d.ts +1 -1
  154. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  155. package/dist/server/auth.js +1 -1
  156. package/dist/server/auth.js.map +1 -1
  157. package/dist/server/credential-provider.d.ts +5 -3
  158. package/dist/server/credential-provider.d.ts.map +1 -1
  159. package/dist/server/credential-provider.js +7 -3
  160. package/dist/server/credential-provider.js.map +1 -1
  161. package/dist/shared/cache-control.d.ts +8 -6
  162. package/dist/shared/cache-control.d.ts.map +1 -1
  163. package/dist/shared/cache-control.js +8 -3
  164. package/dist/shared/cache-control.js.map +1 -1
  165. package/dist/usage/store.d.ts +21 -0
  166. package/dist/usage/store.d.ts.map +1 -1
  167. package/dist/usage/store.js +54 -19
  168. package/dist/usage/store.js.map +1 -1
  169. package/docs/content/locales/ar-SA/template-design.mdx +13 -0
  170. package/docs/content/locales/de-DE/template-design.mdx +18 -0
  171. package/docs/content/locales/es-ES/template-design.mdx +16 -0
  172. package/docs/content/locales/fr-FR/template-design.mdx +17 -0
  173. package/docs/content/locales/hi-IN/template-design.mdx +16 -0
  174. package/docs/content/locales/ja-JP/template-design.mdx +17 -0
  175. package/docs/content/locales/ko-KR/template-design.mdx +15 -0
  176. package/docs/content/locales/pt-BR/template-design.mdx +16 -0
  177. package/docs/content/locales/zh-CN/template-design.mdx +11 -0
  178. package/docs/content/locales/zh-TW/template-design.mdx +12 -0
  179. package/docs/content/pr-visual-recap.mdx +20 -12
  180. package/docs/content/template-design.mdx +16 -0
  181. package/package.json +2 -2
@@ -152,6 +152,7 @@ async function ensureUsageTable() {
152
152
  cache_read_tokens ${intType()} NOT NULL DEFAULT 0,
153
153
  cache_write_tokens ${intType()} NOT NULL DEFAULT 0,
154
154
  cost_cents_x100 ${intType()} NOT NULL DEFAULT 0,
155
+ cost_source TEXT NOT NULL DEFAULT 'estimated',
155
156
  model TEXT NOT NULL DEFAULT '',
156
157
  label TEXT NOT NULL DEFAULT 'chat',
157
158
  app TEXT NOT NULL DEFAULT '',
@@ -170,6 +171,7 @@ async function ensureUsageTable() {
170
171
  const additions = [
171
172
  ["cache_read_tokens", `${intType()} NOT NULL DEFAULT 0`],
172
173
  ["cache_write_tokens", `${intType()} NOT NULL DEFAULT 0`],
174
+ ["cost_source", `TEXT NOT NULL DEFAULT 'estimated'`],
173
175
  ["label", `TEXT NOT NULL DEFAULT 'chat'`],
174
176
  ["app", `TEXT NOT NULL DEFAULT ''`],
175
177
  ["ref_id", `TEXT NOT NULL DEFAULT ''`],
@@ -258,7 +260,7 @@ export async function recordUsage(recordOrOwner, inputTokens, outputTokens, mode
258
260
  model: model ?? "",
259
261
  }
260
262
  : recordOrOwner;
261
- const { ownerEmail, inputTokens: inTok, outputTokens: outTok, cacheReadTokens = 0, cacheWriteTokens = 0, model: modelName, label, app, refId, costCentsX100, orgId, runId, threadId, taskId, integrationScopeId, sourcePlatform, sourceId, } = record;
263
+ const { ownerEmail, inputTokens: inTok, outputTokens: outTok, cacheReadTokens = 0, cacheWriteTokens = 0, model: modelName, label, app, refId, costCentsX100, costSource, orgId, runId, threadId, taskId, integrationScopeId, sourcePlatform, sourceId, } = record;
262
264
  // Skip no-op writes (e.g. a stream aborted before any tokens flowed)
263
265
  if (!inTok && !outTok && !cacheReadTokens && !cacheWriteTokens)
264
266
  return;
@@ -278,13 +280,16 @@ export async function recordUsage(recordOrOwner, inputTokens, outputTokens, mode
278
280
  }
279
281
  // Prefer an explicit precomputed cost (e.g. a provider-reported dollar cost);
280
282
  // otherwise derive it from tokens via the pricing table.
281
- const costX100 = costCentsX100 ??
282
- calculateCost(inTok, outTok, modelName, cacheReadTokens, cacheWriteTokens);
283
+ const resolvedCostSource = costSource ?? (costCentsX100 == null ? "estimated" : "reported");
284
+ const costX100 = resolvedCostSource === "unavailable"
285
+ ? 0
286
+ : (costCentsX100 ??
287
+ calculateCost(inTok, outTok, modelName, cacheReadTokens, cacheWriteTokens));
283
288
  const id = Date.now() * 1000 + Math.floor(Math.random() * 1000);
284
289
  await client.execute({
285
290
  sql: `INSERT INTO token_usage
286
- (id, owner_email, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost_cents_x100, model, label, app, ref_id, org_id, run_id, thread_id, task_id, integration_scope_id, source_platform, source_id, created_at)
287
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
291
+ (id, owner_email, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost_cents_x100, cost_source, model, label, app, ref_id, org_id, run_id, thread_id, task_id, integration_scope_id, source_platform, source_id, created_at)
292
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
288
293
  args: [
289
294
  id,
290
295
  ownerEmail,
@@ -293,6 +298,7 @@ export async function recordUsage(recordOrOwner, inputTokens, outputTokens, mode
293
298
  cacheReadTokens,
294
299
  cacheWriteTokens,
295
300
  costX100,
301
+ resolvedCostSource,
296
302
  modelName,
297
303
  resolvedLabel,
298
304
  resolvedApp,
@@ -330,7 +336,8 @@ export async function getUsageSummary(options) {
330
336
  const sinceMs = options.sinceMs ?? Date.now() - 30 * DAY_MS;
331
337
  const totalRow = await client.execute({
332
338
  sql: `SELECT
333
- COALESCE(SUM(cost_cents_x100), 0) AS cents,
339
+ COALESCE(SUM(CASE WHEN cost_source = 'unavailable' THEN 0 ELSE cost_cents_x100 END), 0) AS known_cents,
340
+ COALESCE(SUM(CASE WHEN cost_source = 'unavailable' THEN 1 ELSE 0 END), 0) AS unavailable_calls,
334
341
  COUNT(*) AS calls,
335
342
  COALESCE(SUM(input_tokens), 0) AS in_tok,
336
343
  COALESCE(SUM(output_tokens), 0) AS out_tok,
@@ -342,7 +349,8 @@ export async function getUsageSummary(options) {
342
349
  const t = (totalRow.rows[0] ?? {});
343
350
  const bucketSql = (col) => ({
344
351
  sql: `SELECT ${col} AS k,
345
- COALESCE(SUM(cost_cents_x100), 0) AS cents,
352
+ COALESCE(SUM(CASE WHEN cost_source = 'unavailable' THEN 0 ELSE cost_cents_x100 END), 0) AS known_cents,
353
+ COALESCE(SUM(CASE WHEN cost_source = 'unavailable' THEN 1 ELSE 0 END), 0) AS unavailable_calls,
346
354
  COUNT(*) AS calls,
347
355
  COALESCE(SUM(input_tokens), 0) AS in_tok,
348
356
  COALESCE(SUM(output_tokens), 0) AS out_tok,
@@ -351,14 +359,17 @@ export async function getUsageSummary(options) {
351
359
  FROM token_usage
352
360
  WHERE owner_email = ? AND created_at >= ?
353
361
  GROUP BY ${col}
354
- ORDER BY cents DESC`,
362
+ ORDER BY known_cents DESC`,
355
363
  args: [options.ownerEmail, sinceMs],
356
364
  });
357
365
  const mapBuckets = (rows) => rows.map((r) => {
358
366
  const row = r;
367
+ const knownCents = Number(row.known_cents ?? 0) / 100;
368
+ const unavailableCalls = Number(row.unavailable_calls ?? 0);
359
369
  return {
360
370
  key: String(row.k ?? ""),
361
- cents: Number(row.cents ?? 0) / 100,
371
+ cents: knownCents,
372
+ cost: buildUsageCostAggregate(knownCents, unavailableCalls),
362
373
  calls: Number(row.calls ?? 0),
363
374
  inputTokens: Number(row.in_tok ?? 0),
364
375
  outputTokens: Number(row.out_tok ?? 0),
@@ -375,29 +386,40 @@ export async function getUsageSummary(options) {
375
386
  // date functions (SQLite `strftime`, Postgres `to_char`). Cheap enough
376
387
  // for a 30-day window; if this grows, swap for a dialect-aware query.
377
388
  const dayRows = await client.execute({
378
- sql: `SELECT created_at, cost_cents_x100 FROM token_usage
389
+ sql: `SELECT created_at, cost_cents_x100, cost_source FROM token_usage
379
390
  WHERE owner_email = ? AND created_at >= ?`,
380
391
  args: [options.ownerEmail, sinceMs],
381
392
  });
382
393
  const dayMap = new Map();
383
394
  for (const row of dayRows.rows) {
384
395
  const date = new Date(Number(row.created_at)).toISOString().slice(0, 10);
385
- const prev = dayMap.get(date) ?? { cents: 0, calls: 0 };
386
- prev.cents += Number(row.cost_cents_x100 ?? 0);
396
+ const prev = dayMap.get(date) ?? {
397
+ knownCentsX100: 0,
398
+ unavailableCalls: 0,
399
+ calls: 0,
400
+ };
401
+ if (row.cost_source === "unavailable")
402
+ prev.unavailableCalls += 1;
403
+ else
404
+ prev.knownCentsX100 += Number(row.cost_cents_x100 ?? 0);
387
405
  prev.calls += 1;
388
406
  dayMap.set(date, prev);
389
407
  }
390
408
  const byDay = [...dayMap.entries()]
391
- .map(([date, v]) => ({
392
- date,
393
- cents: v.cents / 100,
394
- calls: v.calls,
395
- }))
409
+ .map(([date, v]) => {
410
+ const knownCents = v.knownCentsX100 / 100;
411
+ return {
412
+ date,
413
+ cents: knownCents,
414
+ cost: buildUsageCostAggregate(knownCents, v.unavailableCalls),
415
+ calls: v.calls,
416
+ };
417
+ })
396
418
  .sort((a, b) => a.date.localeCompare(b.date));
397
419
  const recentRows = await client.execute({
398
420
  sql: `SELECT id, created_at, label, app, model,
399
421
  input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
400
- cost_cents_x100
422
+ cost_cents_x100, cost_source
401
423
  FROM token_usage
402
424
  WHERE owner_email = ?
403
425
  ORDER BY created_at DESC
@@ -415,10 +437,14 @@ export async function getUsageSummary(options) {
415
437
  cacheReadTokens: Number(row.cache_read_tokens ?? 0),
416
438
  cacheWriteTokens: Number(row.cache_write_tokens ?? 0),
417
439
  cents: Number(row.cost_cents_x100 ?? 0) / 100,
440
+ costSource: String(row.cost_source ?? "estimated"),
418
441
  }));
442
+ const knownCents = Number(t.known_cents ?? 0) / 100;
443
+ const unavailableCalls = Number(t.unavailable_calls ?? 0);
419
444
  return {
420
445
  billing: USD_USAGE_BILLING,
421
- totalCents: Number(t.cents ?? 0) / 100,
446
+ totalCents: knownCents,
447
+ totalCost: buildUsageCostAggregate(knownCents, unavailableCalls),
422
448
  totalCalls: Number(t.calls ?? 0),
423
449
  totalInputTokens: Number(t.in_tok ?? 0),
424
450
  totalOutputTokens: Number(t.out_tok ?? 0),
@@ -432,4 +458,13 @@ export async function getUsageSummary(options) {
432
458
  recent,
433
459
  };
434
460
  }
461
+ function buildUsageCostAggregate(knownCents, unavailableCalls) {
462
+ if (unavailableCalls === 0) {
463
+ return { status: "known", knownCents, unavailableCalls: 0 };
464
+ }
465
+ if (knownCents === 0) {
466
+ return { status: "unavailable", knownCents: 0, unavailableCalls };
467
+ }
468
+ return { status: "partial", knownCents, unavailableCalls };
469
+ }
435
470
  //# sourceMappingURL=store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/usage/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Token usage tracking and cost monitoring.\n *\n * Every LLM call made by the framework records a row here so users can\n * see where their spend is going — chat vs automations vs background jobs\n * vs whatever else a template labels its prompts as.\n *\n * Cost is stored as \"centicents\" (1/100th of a cent) for integer precision.\n */\nimport { getDbExec, intType, isPostgres } from \"../db/client.js\";\nimport {\n ensureColumnExists,\n ensureIndexExists,\n ensureTableExists,\n} from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\n\n/**\n * Per-million-token pricing in cents. Cache read is typically ~10% of\n * input; cache write (5m TTL) is ~125%. Pricing is best-effort — keep\n * this table in sync with Anthropic's published prices.\n */\ninterface ModelPricing {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n}\n\nexport const BUILDER_AGENT_CREDIT_MARGIN_MULTIPLIER = 1.25;\nexport const BUILDER_AGENT_CREDITS_PER_USD = 20;\n\nexport type UsageBillingUnit = \"usd\" | \"builder-credits\";\n\nexport interface UsageBillingMode {\n unit: UsageBillingUnit;\n label: string;\n shortLabel: string;\n source: \"estimated-provider-cost\" | \"builder-agent-credits\";\n hardCostMarginMultiplier?: number;\n creditsPerUsd?: number;\n}\n\nexport const USD_USAGE_BILLING: UsageBillingMode = {\n unit: \"usd\",\n label: \"Estimated spend\",\n shortLabel: \"Cost\",\n source: \"estimated-provider-cost\",\n};\n\nexport const BUILDER_CREDIT_USAGE_BILLING: UsageBillingMode = {\n unit: \"builder-credits\",\n label: \"Builder.io credit spend\",\n shortLabel: \"Credits\",\n source: \"builder-agent-credits\",\n hardCostMarginMultiplier: BUILDER_AGENT_CREDIT_MARGIN_MULTIPLIER,\n creditsPerUsd: BUILDER_AGENT_CREDITS_PER_USD,\n};\n\nexport function usageBillingForEngine(\n engineName: string | null | undefined,\n): UsageBillingMode {\n return engineName === \"builder\"\n ? BUILDER_CREDIT_USAGE_BILLING\n : USD_USAGE_BILLING;\n}\n\nexport function builderCreditsFromCostCents(cents: number): number {\n if (!Number.isFinite(cents) || cents <= 0) return 0;\n const dollars = cents / 100;\n const credits =\n dollars *\n BUILDER_AGENT_CREDIT_MARGIN_MULTIPLIER *\n BUILDER_AGENT_CREDITS_PER_USD;\n return Math.ceil(credits * 1000) / 1000;\n}\n\nconst PRICING: Array<{ match: RegExp; pricing: ModelPricing }> = [\n // ── Anthropic ──────────────────────────────────────────────────────────────\n // claude-fable-5: $10/$50 per MTok (Mythos-class, launched 2026-06-09)\n {\n match: /fable-5/i,\n pricing: { input: 1000, output: 5000, cacheRead: 100, cacheWrite: 1250 },\n },\n // claude-opus-4-8: $5/$25 standard mode (fast mode same as fable-5 $10/$50)\n // Use standard-mode pricing as default; fast-mode is a separate model id.\n {\n match: /opus-4-8/i,\n pricing: { input: 500, output: 2500, cacheRead: 50, cacheWrite: 625 },\n },\n // claude-opus-4-7 and older opus: ~$15/$75 per MTok\n {\n match: /opus/i,\n pricing: { input: 1500, output: 7500, cacheRead: 150, cacheWrite: 1875 },\n },\n {\n match: /haiku/i,\n pricing: { input: 100, output: 500, cacheRead: 10, cacheWrite: 125 },\n },\n // ── OpenAI / Codex ──────────────────────────────────────────────────────────\n // Published rates as of 2026-07; OpenAI bills cached input at a discount\n // and has no separate cache-write token charge, so cacheWrite is 0. Sol and\n // Terra have higher large-context rates after 272K tokens; this table tracks\n // the standard rate because usage rows do not preserve request context size.\n {\n match: /gpt-5[.-]6-sol/i,\n pricing: { input: 500, output: 3000, cacheRead: 50, cacheWrite: 0 },\n },\n {\n match: /gpt-5[.-]6-terra/i,\n pricing: { input: 250, output: 1500, cacheRead: 25, cacheWrite: 0 },\n },\n {\n match: /gpt-5[.-]6-luna/i,\n pricing: { input: 100, output: 600, cacheRead: 10, cacheWrite: 0 },\n },\n {\n match: /gpt-5/i,\n pricing: { input: 125, output: 1000, cacheRead: 12.5, cacheWrite: 0 },\n },\n // ── Google Gemini ───────────────────────────────────────────────────────────\n // Gemini 3 Pro / 3.1 Pro: ~$1.25/$10 per MTok (approximate; verify on billing)\n {\n match: /gemini-3[.-]1-pro/i,\n pricing: { input: 125, output: 1000, cacheRead: 31, cacheWrite: 0 },\n },\n // Gemini 3.5 Flash / 3 Flash: ~$0.15/$0.60 per MTok\n {\n match: /gemini-3[.-][0-9]+-flash/i,\n pricing: { input: 15, output: 60, cacheRead: 4, cacheWrite: 0 },\n },\n // Gemini 2.5 Pro/Flash (catch-all for older 2.5)\n {\n match: /gemini-2\\.5-pro/i,\n pricing: { input: 125, output: 1000, cacheRead: 31, cacheWrite: 0 },\n },\n {\n match: /gemini-2\\.5-flash/i,\n pricing: { input: 15, output: 60, cacheRead: 4, cacheWrite: 0 },\n },\n // ── Groq ────────────────────────────────────────────────────────────────────\n // Groq Llama 3.3 70B: $0.59/$0.79 per MTok\n {\n match: /llama-3\\.3-70b/i,\n pricing: { input: 59, output: 79, cacheRead: 0, cacheWrite: 0 },\n },\n // Groq Llama 3.1 8B instant: $0.05/$0.08 per MTok\n {\n match: /llama-3\\.1-8b|llama3-8b/i,\n pricing: { input: 5, output: 8, cacheRead: 0, cacheWrite: 0 },\n },\n // ── Mistral ─────────────────────────────────────────────────────────────────\n // Mistral Large: ~$2/$6 per MTok\n {\n match: /mistral-large/i,\n pricing: { input: 200, output: 600, cacheRead: 0, cacheWrite: 0 },\n },\n // Mistral Small/Medium: ~$0.2/$0.6 per MTok\n {\n match: /mistral-small|mistral-medium/i,\n pricing: { input: 20, output: 60, cacheRead: 0, cacheWrite: 0 },\n },\n // default → sonnet pricing ($3/$15 per MTok)\n {\n match: /.*/,\n pricing: { input: 300, output: 1500, cacheRead: 30, cacheWrite: 375 },\n },\n];\n\nfunction pricingFor(model: string): ModelPricing {\n for (const entry of PRICING) {\n if (entry.match.test(model)) return entry.pricing;\n }\n return PRICING[PRICING.length - 1]!.pricing;\n}\n\nexport interface UsageRecord {\n ownerEmail: string;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n model: string;\n /** Category for this call — e.g. \"chat\", \"automation\", \"job\", \"custom-agent\". */\n label?: string;\n /** Optional template/app name (e.g. \"mail\"). Falls back to AGENT_APP / APP_NAME env. */\n app?: string;\n /**\n * Stable id of the thing this usage belongs to (e.g. a recap plan id). When\n * set, any prior row(s) with the same (label, refId) are deleted before\n * insert, so re-recording the same run overwrites instead of double-counting.\n */\n refId?: string;\n /**\n * Precomputed cost in centicents (1/100¢). When provided, it is stored\n * verbatim instead of being derived from tokens — e.g. to mirror a\n * provider-reported dollar cost so two surfaces agree exactly.\n */\n costCentsX100?: number;\n orgId?: string;\n runId?: string;\n threadId?: string;\n taskId?: string;\n integrationScopeId?: string;\n sourcePlatform?: string;\n sourceId?: string;\n}\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureUsageTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS token_usage (\n id ${intType()} PRIMARY KEY,\n owner_email TEXT NOT NULL,\n input_tokens ${intType()} NOT NULL DEFAULT 0,\n output_tokens ${intType()} NOT NULL DEFAULT 0,\n cache_read_tokens ${intType()} NOT NULL DEFAULT 0,\n cache_write_tokens ${intType()} NOT NULL DEFAULT 0,\n cost_cents_x100 ${intType()} NOT NULL DEFAULT 0,\n model TEXT NOT NULL DEFAULT '',\n label TEXT NOT NULL DEFAULT 'chat',\n app TEXT NOT NULL DEFAULT '',\n ref_id TEXT NOT NULL DEFAULT '',\n org_id TEXT,\n run_id TEXT,\n thread_id TEXT,\n task_id TEXT,\n integration_scope_id TEXT,\n source_platform TEXT,\n source_id TEXT,\n created_at ${intType()} NOT NULL\n )\n `;\n\n // Additive columns for older deployments that pre-date the label/cache fields.\n const additions: Array<[string, string]> = [\n [\"cache_read_tokens\", `${intType()} NOT NULL DEFAULT 0`],\n [\"cache_write_tokens\", `${intType()} NOT NULL DEFAULT 0`],\n [\"label\", `TEXT NOT NULL DEFAULT 'chat'`],\n [\"app\", `TEXT NOT NULL DEFAULT ''`],\n [\"ref_id\", `TEXT NOT NULL DEFAULT ''`],\n [\"org_id\", \"TEXT\"],\n [\"run_id\", \"TEXT\"],\n [\"thread_id\", \"TEXT\"],\n [\"task_id\", \"TEXT\"],\n [\"integration_scope_id\", \"TEXT\"],\n [\"source_platform\", \"TEXT\"],\n [\"source_id\", \"TEXT\"],\n ];\n\n if (isPostgres()) {\n // Hot path: the `token_usage` table and its index are virtually always\n // already present in production. Issuing `CREATE TABLE`/`ALTER TABLE`/\n // `CREATE INDEX` still takes a lock that, in a fresh background-worker\n // process behind a concurrent connection on the shared Neon DB, can\n // block ~indefinitely. The ensure* wrappers probe `information_schema`/\n // `pg_indexes` first (plain reads, no lock) and run DDL ONLY for what is\n // actually missing, bounded by a transaction-scoped `lock_timeout`. If a\n // swallowed lock-timeout leaves the schema still missing they RE-PROBE\n // and THROW rather than letting init memoize success against absent\n // schema.\n await ensureTableExists(\"token_usage\", createSql);\n // Add columns on older deployments — guarded so the hot path (columns\n // already present) skips the ACCESS EXCLUSIVE ALTER.\n for (const [col, def] of additions) {\n await ensureColumnExists(\n \"token_usage\",\n col,\n `ALTER TABLE token_usage ADD COLUMN IF NOT EXISTS ${col} ${def}`,\n );\n }\n // Older deployments created `created_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written per run by recordUsage() overflows int4. Widen\n // it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"token_usage\", [\"created_at\"]);\n // Probe pg_indexes first (no lock) and skip the SHARE-locking CREATE\n // INDEX when the index is already present.\n await ensureIndexExists(\n \"idx_token_usage_owner_created\",\n `CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // Add columns on older deployments that pre-date the label/cache\n // fields. Each ALTER is wrapped so a dialect without IF NOT EXISTS\n // (SQLite) still makes progress if only some columns are missing.\n for (const [col, def] of additions) {\n try {\n await client.execute(\n `ALTER TABLE token_usage ADD COLUMN ${col} ${def}`,\n );\n } catch {\n // Column already exists — ignore\n }\n }\n // Older deployments created `created_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written per run by recordUsage() overflows int4. Widen\n // it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"token_usage\", [\"created_at\"]);\n try {\n await client.execute(\n `CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`,\n );\n } catch {}\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\n/**\n * Calculate cost in centicents (1/100th of a cent).\n * Accepts cache tokens so callers that use prompt caching are priced\n * correctly. Non-cache-aware callers can pass 0 for the cache fields.\n */\nexport function calculateCost(\n inputTokens: number,\n outputTokens: number,\n model: string,\n cacheReadTokens = 0,\n cacheWriteTokens = 0,\n): number {\n const p = pricingFor(model);\n const rawCenticents =\n (inputTokens / 1_000_000) * p.input * 100 +\n (outputTokens / 1_000_000) * p.output * 100 +\n (cacheReadTokens / 1_000_000) * p.cacheRead * 100 +\n (cacheWriteTokens / 1_000_000) * p.cacheWrite * 100;\n return rawCenticents > 0 ? Math.max(1, Math.round(rawCenticents)) : 0;\n}\n\n/**\n * Record token usage from an LLM call.\n *\n * Accepts an object with the full set of fields. A positional overload\n * remains for backward compatibility with the older 4-arg signature.\n */\nexport async function recordUsage(record: UsageRecord): Promise<void>;\nexport async function recordUsage(\n ownerEmail: string,\n inputTokens: number,\n outputTokens: number,\n model: string,\n): Promise<void>;\nexport async function recordUsage(\n recordOrOwner: UsageRecord | string,\n inputTokens?: number,\n outputTokens?: number,\n model?: string,\n): Promise<void> {\n const record: UsageRecord =\n typeof recordOrOwner === \"string\"\n ? {\n ownerEmail: recordOrOwner,\n inputTokens: inputTokens ?? 0,\n outputTokens: outputTokens ?? 0,\n model: model ?? \"\",\n }\n : recordOrOwner;\n\n const {\n ownerEmail,\n inputTokens: inTok,\n outputTokens: outTok,\n cacheReadTokens = 0,\n cacheWriteTokens = 0,\n model: modelName,\n label,\n app,\n refId,\n costCentsX100,\n orgId,\n runId,\n threadId,\n taskId,\n integrationScopeId,\n sourcePlatform,\n sourceId,\n } = record;\n\n // Skip no-op writes (e.g. a stream aborted before any tokens flowed)\n if (!inTok && !outTok && !cacheReadTokens && !cacheWriteTokens) return;\n\n await ensureUsageTable();\n const client = getDbExec();\n const resolvedApp =\n app ?? process.env.AGENT_APP ?? process.env.APP_NAME ?? \"\";\n const resolvedLabel = label ?? \"chat\";\n const resolvedRef = refId ?? \"\";\n\n // Replace any prior usage for this (label, refId) so re-recording the same\n // run — e.g. a recap regenerated on a PR re-push — overwrites instead of\n // double-counting. No-op when refId is unset (the common per-call path).\n if (resolvedRef) {\n await client.execute({\n sql: `DELETE FROM token_usage WHERE label = ? AND ref_id = ?`,\n args: [resolvedLabel, resolvedRef],\n });\n }\n\n // Prefer an explicit precomputed cost (e.g. a provider-reported dollar cost);\n // otherwise derive it from tokens via the pricing table.\n const costX100 =\n costCentsX100 ??\n calculateCost(inTok, outTok, modelName, cacheReadTokens, cacheWriteTokens);\n const id = Date.now() * 1000 + Math.floor(Math.random() * 1000);\n await client.execute({\n sql: `INSERT INTO token_usage\n (id, owner_email, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost_cents_x100, model, label, app, ref_id, org_id, run_id, thread_id, task_id, integration_scope_id, source_platform, source_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n ownerEmail,\n inTok,\n outTok,\n cacheReadTokens,\n cacheWriteTokens,\n costX100,\n modelName,\n resolvedLabel,\n resolvedApp,\n resolvedRef,\n orgId ?? null,\n runId ?? null,\n threadId ?? null,\n taskId ?? null,\n integrationScopeId ?? null,\n sourcePlatform ?? null,\n sourceId ?? null,\n Date.now(),\n ],\n });\n}\n\n/** Total cost (in cents) charged against a user, across all time. */\nexport async function getUserUsageCents(ownerEmail: string): Promise<number> {\n await ensureUsageTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT COALESCE(SUM(cost_cents_x100), 0) as total FROM token_usage WHERE owner_email = ?`,\n args: [ownerEmail],\n });\n const total = Number((rows[0] as { total?: number })?.total ?? 0);\n return total / 100;\n}\n\n// ─── Admin / UI queries ─────────────────────────────────────────────────\n\nexport interface UsageSummaryOptions {\n ownerEmail: string;\n /** Inclusive lower bound (ms since epoch). Defaults to 30 days ago. */\n sinceMs?: number;\n}\n\nexport interface UsageBucket {\n key: string;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n cents: number;\n calls: number;\n}\n\nexport interface DailyBucket {\n /** YYYY-MM-DD (UTC) */\n date: string;\n cents: number;\n calls: number;\n}\n\nexport interface UsageRecentEntry {\n id: number;\n createdAt: number;\n label: string;\n app: string;\n model: string;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n cents: number;\n}\n\nexport interface UsageSummary {\n billing?: UsageBillingMode;\n totalCents: number;\n totalCalls: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheReadTokens: number;\n totalCacheWriteTokens: number;\n sinceMs: number;\n byLabel: UsageBucket[];\n byModel: UsageBucket[];\n byApp: UsageBucket[];\n byDay: DailyBucket[];\n recent: UsageRecentEntry[];\n}\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Produce an aggregated spend view for the Usage admin panel.\n * Scoped to the passed owner email; the UI always passes the session user.\n */\nexport async function getUsageSummary(\n options: UsageSummaryOptions,\n): Promise<UsageSummary> {\n await ensureUsageTable();\n const client = getDbExec();\n const sinceMs = options.sinceMs ?? Date.now() - 30 * DAY_MS;\n\n const totalRow = await client.execute({\n sql: `SELECT\n COALESCE(SUM(cost_cents_x100), 0) AS cents,\n COUNT(*) AS calls,\n COALESCE(SUM(input_tokens), 0) AS in_tok,\n COALESCE(SUM(output_tokens), 0) AS out_tok,\n COALESCE(SUM(cache_read_tokens), 0) AS cr_tok,\n COALESCE(SUM(cache_write_tokens), 0) AS cw_tok\n FROM token_usage WHERE owner_email = ? AND created_at >= ?`,\n args: [options.ownerEmail, sinceMs],\n });\n const t = (totalRow.rows[0] ?? {}) as Record<string, number | null>;\n\n const bucketSql = (col: string) => ({\n sql: `SELECT ${col} AS k,\n COALESCE(SUM(cost_cents_x100), 0) AS cents,\n COUNT(*) AS calls,\n COALESCE(SUM(input_tokens), 0) AS in_tok,\n COALESCE(SUM(output_tokens), 0) AS out_tok,\n COALESCE(SUM(cache_read_tokens), 0) AS cr_tok,\n COALESCE(SUM(cache_write_tokens), 0) AS cw_tok\n FROM token_usage\n WHERE owner_email = ? AND created_at >= ?\n GROUP BY ${col}\n ORDER BY cents DESC`,\n args: [options.ownerEmail, sinceMs],\n });\n\n const mapBuckets = (rows: unknown[]): UsageBucket[] =>\n rows.map((r) => {\n const row = r as Record<string, number | string | null>;\n return {\n key: String(row.k ?? \"\"),\n cents: Number(row.cents ?? 0) / 100,\n calls: Number(row.calls ?? 0),\n inputTokens: Number(row.in_tok ?? 0),\n outputTokens: Number(row.out_tok ?? 0),\n cacheReadTokens: Number(row.cr_tok ?? 0),\n cacheWriteTokens: Number(row.cw_tok ?? 0),\n };\n });\n\n const [byLabelR, byModelR, byAppR] = await Promise.all([\n client.execute(bucketSql(\"label\")),\n client.execute(bucketSql(\"model\")),\n client.execute(bucketSql(\"app\")),\n ]);\n\n // By-day aggregation — done in JS so we don't depend on dialect-specific\n // date functions (SQLite `strftime`, Postgres `to_char`). Cheap enough\n // for a 30-day window; if this grows, swap for a dialect-aware query.\n const dayRows = await client.execute({\n sql: `SELECT created_at, cost_cents_x100 FROM token_usage\n WHERE owner_email = ? AND created_at >= ?`,\n args: [options.ownerEmail, sinceMs],\n });\n const dayMap = new Map<string, { cents: number; calls: number }>();\n for (const row of dayRows.rows as Array<Record<string, number>>) {\n const date = new Date(Number(row.created_at)).toISOString().slice(0, 10);\n const prev = dayMap.get(date) ?? { cents: 0, calls: 0 };\n prev.cents += Number(row.cost_cents_x100 ?? 0);\n prev.calls += 1;\n dayMap.set(date, prev);\n }\n const byDay: DailyBucket[] = [...dayMap.entries()]\n .map(([date, v]) => ({\n date,\n cents: v.cents / 100,\n calls: v.calls,\n }))\n .sort((a, b) => a.date.localeCompare(b.date));\n\n const recentRows = await client.execute({\n sql: `SELECT id, created_at, label, app, model,\n input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,\n cost_cents_x100\n FROM token_usage\n WHERE owner_email = ?\n ORDER BY created_at DESC\n LIMIT 50`,\n args: [options.ownerEmail],\n });\n const recent: UsageRecentEntry[] = (\n recentRows.rows as Array<Record<string, number | string | null>>\n ).map((row) => ({\n id: Number(row.id),\n createdAt: Number(row.created_at),\n label: String(row.label ?? \"chat\"),\n app: String(row.app ?? \"\"),\n model: String(row.model ?? \"\"),\n inputTokens: Number(row.input_tokens ?? 0),\n outputTokens: Number(row.output_tokens ?? 0),\n cacheReadTokens: Number(row.cache_read_tokens ?? 0),\n cacheWriteTokens: Number(row.cache_write_tokens ?? 0),\n cents: Number(row.cost_cents_x100 ?? 0) / 100,\n }));\n\n return {\n billing: USD_USAGE_BILLING,\n totalCents: Number(t.cents ?? 0) / 100,\n totalCalls: Number(t.calls ?? 0),\n totalInputTokens: Number(t.in_tok ?? 0),\n totalOutputTokens: Number(t.out_tok ?? 0),\n totalCacheReadTokens: Number(t.cr_tok ?? 0),\n totalCacheWriteTokens: Number(t.cw_tok ?? 0),\n sinceMs,\n byLabel: mapBuckets(byLabelR.rows),\n byModel: mapBuckets(byModelR.rows),\n byApp: mapBuckets(byAppR.rows),\n byDay,\n recent,\n };\n}\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/usage/store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Token usage tracking and cost monitoring.\n *\n * Every LLM call made by the framework records a row here so users can\n * see where their spend is going — chat vs automations vs background jobs\n * vs whatever else a template labels its prompts as.\n *\n * Cost is stored as \"centicents\" (1/100th of a cent) for integer precision.\n */\nimport { getDbExec, intType, isPostgres } from \"../db/client.js\";\nimport {\n ensureColumnExists,\n ensureIndexExists,\n ensureTableExists,\n} from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\n\n/**\n * Per-million-token pricing in cents. Cache read is typically ~10% of\n * input; cache write (5m TTL) is ~125%. Pricing is best-effort — keep\n * this table in sync with Anthropic's published prices.\n */\ninterface ModelPricing {\n input: number;\n output: number;\n cacheRead: number;\n cacheWrite: number;\n}\n\nexport const BUILDER_AGENT_CREDIT_MARGIN_MULTIPLIER = 1.25;\nexport const BUILDER_AGENT_CREDITS_PER_USD = 20;\n\nexport type UsageBillingUnit = \"usd\" | \"builder-credits\";\n\nexport interface UsageBillingMode {\n unit: UsageBillingUnit;\n label: string;\n shortLabel: string;\n source: \"estimated-provider-cost\" | \"builder-agent-credits\";\n hardCostMarginMultiplier?: number;\n creditsPerUsd?: number;\n}\n\nexport const USD_USAGE_BILLING: UsageBillingMode = {\n unit: \"usd\",\n label: \"Estimated spend\",\n shortLabel: \"Cost\",\n source: \"estimated-provider-cost\",\n};\n\nexport const BUILDER_CREDIT_USAGE_BILLING: UsageBillingMode = {\n unit: \"builder-credits\",\n label: \"Builder.io credit spend\",\n shortLabel: \"Credits\",\n source: \"builder-agent-credits\",\n hardCostMarginMultiplier: BUILDER_AGENT_CREDIT_MARGIN_MULTIPLIER,\n creditsPerUsd: BUILDER_AGENT_CREDITS_PER_USD,\n};\n\nexport function usageBillingForEngine(\n engineName: string | null | undefined,\n): UsageBillingMode {\n return engineName === \"builder\"\n ? BUILDER_CREDIT_USAGE_BILLING\n : USD_USAGE_BILLING;\n}\n\nexport function builderCreditsFromCostCents(cents: number): number {\n if (!Number.isFinite(cents) || cents <= 0) return 0;\n const dollars = cents / 100;\n const credits =\n dollars *\n BUILDER_AGENT_CREDIT_MARGIN_MULTIPLIER *\n BUILDER_AGENT_CREDITS_PER_USD;\n return Math.ceil(credits * 1000) / 1000;\n}\n\nconst PRICING: Array<{ match: RegExp; pricing: ModelPricing }> = [\n // ── Anthropic ──────────────────────────────────────────────────────────────\n // claude-fable-5: $10/$50 per MTok (Mythos-class, launched 2026-06-09)\n {\n match: /fable-5/i,\n pricing: { input: 1000, output: 5000, cacheRead: 100, cacheWrite: 1250 },\n },\n // claude-opus-4-8: $5/$25 standard mode (fast mode same as fable-5 $10/$50)\n // Use standard-mode pricing as default; fast-mode is a separate model id.\n {\n match: /opus-4-8/i,\n pricing: { input: 500, output: 2500, cacheRead: 50, cacheWrite: 625 },\n },\n // claude-opus-4-7 and older opus: ~$15/$75 per MTok\n {\n match: /opus/i,\n pricing: { input: 1500, output: 7500, cacheRead: 150, cacheWrite: 1875 },\n },\n {\n match: /haiku/i,\n pricing: { input: 100, output: 500, cacheRead: 10, cacheWrite: 125 },\n },\n // ── OpenAI / Codex ──────────────────────────────────────────────────────────\n // Published rates as of 2026-07; OpenAI bills cached input at a discount\n // and has no separate cache-write token charge, so cacheWrite is 0. Sol and\n // Terra have higher large-context rates after 272K tokens; this table tracks\n // the standard rate because usage rows do not preserve request context size.\n {\n match: /gpt-5[.-]6-sol/i,\n pricing: { input: 500, output: 3000, cacheRead: 50, cacheWrite: 0 },\n },\n {\n match: /gpt-5[.-]6-terra/i,\n pricing: { input: 250, output: 1500, cacheRead: 25, cacheWrite: 0 },\n },\n {\n match: /gpt-5[.-]6-luna/i,\n pricing: { input: 100, output: 600, cacheRead: 10, cacheWrite: 0 },\n },\n {\n match: /gpt-5/i,\n pricing: { input: 125, output: 1000, cacheRead: 12.5, cacheWrite: 0 },\n },\n // ── Google Gemini ───────────────────────────────────────────────────────────\n // Gemini 3 Pro / 3.1 Pro: ~$1.25/$10 per MTok (approximate; verify on billing)\n {\n match: /gemini-3[.-]1-pro/i,\n pricing: { input: 125, output: 1000, cacheRead: 31, cacheWrite: 0 },\n },\n // Gemini 3.5 Flash / 3 Flash: ~$0.15/$0.60 per MTok\n {\n match: /gemini-3[.-][0-9]+-flash/i,\n pricing: { input: 15, output: 60, cacheRead: 4, cacheWrite: 0 },\n },\n // Gemini 2.5 Pro/Flash (catch-all for older 2.5)\n {\n match: /gemini-2\\.5-pro/i,\n pricing: { input: 125, output: 1000, cacheRead: 31, cacheWrite: 0 },\n },\n {\n match: /gemini-2\\.5-flash/i,\n pricing: { input: 15, output: 60, cacheRead: 4, cacheWrite: 0 },\n },\n // ── Groq ────────────────────────────────────────────────────────────────────\n // Groq Llama 3.3 70B: $0.59/$0.79 per MTok\n {\n match: /llama-3\\.3-70b/i,\n pricing: { input: 59, output: 79, cacheRead: 0, cacheWrite: 0 },\n },\n // Groq Llama 3.1 8B instant: $0.05/$0.08 per MTok\n {\n match: /llama-3\\.1-8b|llama3-8b/i,\n pricing: { input: 5, output: 8, cacheRead: 0, cacheWrite: 0 },\n },\n // ── Mistral ─────────────────────────────────────────────────────────────────\n // Mistral Large: ~$2/$6 per MTok\n {\n match: /mistral-large/i,\n pricing: { input: 200, output: 600, cacheRead: 0, cacheWrite: 0 },\n },\n // Mistral Small/Medium: ~$0.2/$0.6 per MTok\n {\n match: /mistral-small|mistral-medium/i,\n pricing: { input: 20, output: 60, cacheRead: 0, cacheWrite: 0 },\n },\n // default → sonnet pricing ($3/$15 per MTok)\n {\n match: /.*/,\n pricing: { input: 300, output: 1500, cacheRead: 30, cacheWrite: 375 },\n },\n];\n\nfunction pricingFor(model: string): ModelPricing {\n for (const entry of PRICING) {\n if (entry.match.test(model)) return entry.pricing;\n }\n return PRICING[PRICING.length - 1]!.pricing;\n}\n\nexport interface UsageRecord {\n ownerEmail: string;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens?: number;\n cacheWriteTokens?: number;\n model: string;\n /** Category for this call — e.g. \"chat\", \"automation\", \"job\", \"custom-agent\". */\n label?: string;\n /** Optional template/app name (e.g. \"mail\"). Falls back to AGENT_APP / APP_NAME env. */\n app?: string;\n /**\n * Stable id of the thing this usage belongs to (e.g. a recap plan id). When\n * set, any prior row(s) with the same (label, refId) are deleted before\n * insert, so re-recording the same run overwrites instead of double-counting.\n */\n refId?: string;\n /**\n * Precomputed cost in centicents (1/100¢). When provided, it is stored\n * verbatim instead of being derived from tokens — e.g. to mirror a\n * provider-reported dollar cost so two surfaces agree exactly.\n */\n costCentsX100?: number;\n /** Whether cost is provider-reported, estimated, or unavailable. */\n costSource?: UsageCostSource;\n orgId?: string;\n runId?: string;\n threadId?: string;\n taskId?: string;\n integrationScopeId?: string;\n sourcePlatform?: string;\n sourceId?: string;\n}\n\nexport type UsageCostSource = \"reported\" | \"estimated\" | \"unavailable\";\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureUsageTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS token_usage (\n id ${intType()} PRIMARY KEY,\n owner_email TEXT NOT NULL,\n input_tokens ${intType()} NOT NULL DEFAULT 0,\n output_tokens ${intType()} NOT NULL DEFAULT 0,\n cache_read_tokens ${intType()} NOT NULL DEFAULT 0,\n cache_write_tokens ${intType()} NOT NULL DEFAULT 0,\n cost_cents_x100 ${intType()} NOT NULL DEFAULT 0,\n cost_source TEXT NOT NULL DEFAULT 'estimated',\n model TEXT NOT NULL DEFAULT '',\n label TEXT NOT NULL DEFAULT 'chat',\n app TEXT NOT NULL DEFAULT '',\n ref_id TEXT NOT NULL DEFAULT '',\n org_id TEXT,\n run_id TEXT,\n thread_id TEXT,\n task_id TEXT,\n integration_scope_id TEXT,\n source_platform TEXT,\n source_id TEXT,\n created_at ${intType()} NOT NULL\n )\n `;\n\n // Additive columns for older deployments that pre-date the label/cache fields.\n const additions: Array<[string, string]> = [\n [\"cache_read_tokens\", `${intType()} NOT NULL DEFAULT 0`],\n [\"cache_write_tokens\", `${intType()} NOT NULL DEFAULT 0`],\n [\"cost_source\", `TEXT NOT NULL DEFAULT 'estimated'`],\n [\"label\", `TEXT NOT NULL DEFAULT 'chat'`],\n [\"app\", `TEXT NOT NULL DEFAULT ''`],\n [\"ref_id\", `TEXT NOT NULL DEFAULT ''`],\n [\"org_id\", \"TEXT\"],\n [\"run_id\", \"TEXT\"],\n [\"thread_id\", \"TEXT\"],\n [\"task_id\", \"TEXT\"],\n [\"integration_scope_id\", \"TEXT\"],\n [\"source_platform\", \"TEXT\"],\n [\"source_id\", \"TEXT\"],\n ];\n\n if (isPostgres()) {\n // Hot path: the `token_usage` table and its index are virtually always\n // already present in production. Issuing `CREATE TABLE`/`ALTER TABLE`/\n // `CREATE INDEX` still takes a lock that, in a fresh background-worker\n // process behind a concurrent connection on the shared Neon DB, can\n // block ~indefinitely. The ensure* wrappers probe `information_schema`/\n // `pg_indexes` first (plain reads, no lock) and run DDL ONLY for what is\n // actually missing, bounded by a transaction-scoped `lock_timeout`. If a\n // swallowed lock-timeout leaves the schema still missing they RE-PROBE\n // and THROW rather than letting init memoize success against absent\n // schema.\n await ensureTableExists(\"token_usage\", createSql);\n // Add columns on older deployments — guarded so the hot path (columns\n // already present) skips the ACCESS EXCLUSIVE ALTER.\n for (const [col, def] of additions) {\n await ensureColumnExists(\n \"token_usage\",\n col,\n `ALTER TABLE token_usage ADD COLUMN IF NOT EXISTS ${col} ${def}`,\n );\n }\n // Older deployments created `created_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written per run by recordUsage() overflows int4. Widen\n // it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"token_usage\", [\"created_at\"]);\n // Probe pg_indexes first (no lock) and skip the SHARE-locking CREATE\n // INDEX when the index is already present.\n await ensureIndexExists(\n \"idx_token_usage_owner_created\",\n `CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // Add columns on older deployments that pre-date the label/cache\n // fields. Each ALTER is wrapped so a dialect without IF NOT EXISTS\n // (SQLite) still makes progress if only some columns are missing.\n for (const [col, def] of additions) {\n try {\n await client.execute(\n `ALTER TABLE token_usage ADD COLUMN ${col} ${def}`,\n );\n } catch {\n // Column already exists — ignore\n }\n }\n // Older deployments created `created_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written per run by recordUsage() overflows int4. Widen\n // it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"token_usage\", [\"created_at\"]);\n try {\n await client.execute(\n `CREATE INDEX IF NOT EXISTS idx_token_usage_owner_created ON token_usage (owner_email, created_at)`,\n );\n } catch {}\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\n/**\n * Calculate cost in centicents (1/100th of a cent).\n * Accepts cache tokens so callers that use prompt caching are priced\n * correctly. Non-cache-aware callers can pass 0 for the cache fields.\n */\nexport function calculateCost(\n inputTokens: number,\n outputTokens: number,\n model: string,\n cacheReadTokens = 0,\n cacheWriteTokens = 0,\n): number {\n const p = pricingFor(model);\n const rawCenticents =\n (inputTokens / 1_000_000) * p.input * 100 +\n (outputTokens / 1_000_000) * p.output * 100 +\n (cacheReadTokens / 1_000_000) * p.cacheRead * 100 +\n (cacheWriteTokens / 1_000_000) * p.cacheWrite * 100;\n return rawCenticents > 0 ? Math.max(1, Math.round(rawCenticents)) : 0;\n}\n\n/**\n * Record token usage from an LLM call.\n *\n * Accepts an object with the full set of fields. A positional overload\n * remains for backward compatibility with the older 4-arg signature.\n */\nexport async function recordUsage(record: UsageRecord): Promise<void>;\nexport async function recordUsage(\n ownerEmail: string,\n inputTokens: number,\n outputTokens: number,\n model: string,\n): Promise<void>;\nexport async function recordUsage(\n recordOrOwner: UsageRecord | string,\n inputTokens?: number,\n outputTokens?: number,\n model?: string,\n): Promise<void> {\n const record: UsageRecord =\n typeof recordOrOwner === \"string\"\n ? {\n ownerEmail: recordOrOwner,\n inputTokens: inputTokens ?? 0,\n outputTokens: outputTokens ?? 0,\n model: model ?? \"\",\n }\n : recordOrOwner;\n\n const {\n ownerEmail,\n inputTokens: inTok,\n outputTokens: outTok,\n cacheReadTokens = 0,\n cacheWriteTokens = 0,\n model: modelName,\n label,\n app,\n refId,\n costCentsX100,\n costSource,\n orgId,\n runId,\n threadId,\n taskId,\n integrationScopeId,\n sourcePlatform,\n sourceId,\n } = record;\n\n // Skip no-op writes (e.g. a stream aborted before any tokens flowed)\n if (!inTok && !outTok && !cacheReadTokens && !cacheWriteTokens) return;\n\n await ensureUsageTable();\n const client = getDbExec();\n const resolvedApp =\n app ?? process.env.AGENT_APP ?? process.env.APP_NAME ?? \"\";\n const resolvedLabel = label ?? \"chat\";\n const resolvedRef = refId ?? \"\";\n\n // Replace any prior usage for this (label, refId) so re-recording the same\n // run — e.g. a recap regenerated on a PR re-push — overwrites instead of\n // double-counting. No-op when refId is unset (the common per-call path).\n if (resolvedRef) {\n await client.execute({\n sql: `DELETE FROM token_usage WHERE label = ? AND ref_id = ?`,\n args: [resolvedLabel, resolvedRef],\n });\n }\n\n // Prefer an explicit precomputed cost (e.g. a provider-reported dollar cost);\n // otherwise derive it from tokens via the pricing table.\n const resolvedCostSource =\n costSource ?? (costCentsX100 == null ? \"estimated\" : \"reported\");\n const costX100 =\n resolvedCostSource === \"unavailable\"\n ? 0\n : (costCentsX100 ??\n calculateCost(\n inTok,\n outTok,\n modelName,\n cacheReadTokens,\n cacheWriteTokens,\n ));\n const id = Date.now() * 1000 + Math.floor(Math.random() * 1000);\n await client.execute({\n sql: `INSERT INTO token_usage\n (id, owner_email, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, cost_cents_x100, cost_source, model, label, app, ref_id, org_id, run_id, thread_id, task_id, integration_scope_id, source_platform, source_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n ownerEmail,\n inTok,\n outTok,\n cacheReadTokens,\n cacheWriteTokens,\n costX100,\n resolvedCostSource,\n modelName,\n resolvedLabel,\n resolvedApp,\n resolvedRef,\n orgId ?? null,\n runId ?? null,\n threadId ?? null,\n taskId ?? null,\n integrationScopeId ?? null,\n sourcePlatform ?? null,\n sourceId ?? null,\n Date.now(),\n ],\n });\n}\n\n/** Total cost (in cents) charged against a user, across all time. */\nexport async function getUserUsageCents(ownerEmail: string): Promise<number> {\n await ensureUsageTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT COALESCE(SUM(cost_cents_x100), 0) as total FROM token_usage WHERE owner_email = ?`,\n args: [ownerEmail],\n });\n const total = Number((rows[0] as { total?: number })?.total ?? 0);\n return total / 100;\n}\n\n// ─── Admin / UI queries ─────────────────────────────────────────────────\n\nexport interface UsageSummaryOptions {\n ownerEmail: string;\n /** Inclusive lower bound (ms since epoch). Defaults to 30 days ago. */\n sinceMs?: number;\n}\n\nexport interface UsageBucket {\n key: string;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n cents: number;\n cost: UsageCostAggregate;\n calls: number;\n}\n\nexport interface DailyBucket {\n /** YYYY-MM-DD (UTC) */\n date: string;\n cents: number;\n cost: UsageCostAggregate;\n calls: number;\n}\n\nexport type UsageCostAggregate =\n | { status: \"known\"; knownCents: number; unavailableCalls: 0 }\n | { status: \"partial\"; knownCents: number; unavailableCalls: number }\n | { status: \"unavailable\"; knownCents: 0; unavailableCalls: number };\n\nexport interface UsageRecentEntry {\n id: number;\n createdAt: number;\n label: string;\n app: string;\n model: string;\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n cents: number;\n costSource: UsageCostSource;\n}\n\nexport interface UsageSummary {\n billing?: UsageBillingMode;\n /** Legacy known-cost subtotal. Use totalCost to preserve unavailable spend. */\n totalCents: number;\n totalCost: UsageCostAggregate;\n totalCalls: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalCacheReadTokens: number;\n totalCacheWriteTokens: number;\n sinceMs: number;\n byLabel: UsageBucket[];\n byModel: UsageBucket[];\n byApp: UsageBucket[];\n byDay: DailyBucket[];\n recent: UsageRecentEntry[];\n}\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Produce an aggregated spend view for the Usage admin panel.\n * Scoped to the passed owner email; the UI always passes the session user.\n */\nexport async function getUsageSummary(\n options: UsageSummaryOptions,\n): Promise<UsageSummary> {\n await ensureUsageTable();\n const client = getDbExec();\n const sinceMs = options.sinceMs ?? Date.now() - 30 * DAY_MS;\n\n const totalRow = await client.execute({\n sql: `SELECT\n COALESCE(SUM(CASE WHEN cost_source = 'unavailable' THEN 0 ELSE cost_cents_x100 END), 0) AS known_cents,\n COALESCE(SUM(CASE WHEN cost_source = 'unavailable' THEN 1 ELSE 0 END), 0) AS unavailable_calls,\n COUNT(*) AS calls,\n COALESCE(SUM(input_tokens), 0) AS in_tok,\n COALESCE(SUM(output_tokens), 0) AS out_tok,\n COALESCE(SUM(cache_read_tokens), 0) AS cr_tok,\n COALESCE(SUM(cache_write_tokens), 0) AS cw_tok\n FROM token_usage WHERE owner_email = ? AND created_at >= ?`,\n args: [options.ownerEmail, sinceMs],\n });\n const t = (totalRow.rows[0] ?? {}) as Record<string, number | null>;\n\n const bucketSql = (col: string) => ({\n sql: `SELECT ${col} AS k,\n COALESCE(SUM(CASE WHEN cost_source = 'unavailable' THEN 0 ELSE cost_cents_x100 END), 0) AS known_cents,\n COALESCE(SUM(CASE WHEN cost_source = 'unavailable' THEN 1 ELSE 0 END), 0) AS unavailable_calls,\n COUNT(*) AS calls,\n COALESCE(SUM(input_tokens), 0) AS in_tok,\n COALESCE(SUM(output_tokens), 0) AS out_tok,\n COALESCE(SUM(cache_read_tokens), 0) AS cr_tok,\n COALESCE(SUM(cache_write_tokens), 0) AS cw_tok\n FROM token_usage\n WHERE owner_email = ? AND created_at >= ?\n GROUP BY ${col}\n ORDER BY known_cents DESC`,\n args: [options.ownerEmail, sinceMs],\n });\n\n const mapBuckets = (rows: unknown[]): UsageBucket[] =>\n rows.map((r) => {\n const row = r as Record<string, number | string | null>;\n const knownCents = Number(row.known_cents ?? 0) / 100;\n const unavailableCalls = Number(row.unavailable_calls ?? 0);\n return {\n key: String(row.k ?? \"\"),\n cents: knownCents,\n cost: buildUsageCostAggregate(knownCents, unavailableCalls),\n calls: Number(row.calls ?? 0),\n inputTokens: Number(row.in_tok ?? 0),\n outputTokens: Number(row.out_tok ?? 0),\n cacheReadTokens: Number(row.cr_tok ?? 0),\n cacheWriteTokens: Number(row.cw_tok ?? 0),\n };\n });\n\n const [byLabelR, byModelR, byAppR] = await Promise.all([\n client.execute(bucketSql(\"label\")),\n client.execute(bucketSql(\"model\")),\n client.execute(bucketSql(\"app\")),\n ]);\n\n // By-day aggregation — done in JS so we don't depend on dialect-specific\n // date functions (SQLite `strftime`, Postgres `to_char`). Cheap enough\n // for a 30-day window; if this grows, swap for a dialect-aware query.\n const dayRows = await client.execute({\n sql: `SELECT created_at, cost_cents_x100, cost_source FROM token_usage\n WHERE owner_email = ? AND created_at >= ?`,\n args: [options.ownerEmail, sinceMs],\n });\n const dayMap = new Map<\n string,\n { knownCentsX100: number; unavailableCalls: number; calls: number }\n >();\n for (const row of dayRows.rows as Array<Record<string, number | string>>) {\n const date = new Date(Number(row.created_at)).toISOString().slice(0, 10);\n const prev = dayMap.get(date) ?? {\n knownCentsX100: 0,\n unavailableCalls: 0,\n calls: 0,\n };\n if (row.cost_source === \"unavailable\") prev.unavailableCalls += 1;\n else prev.knownCentsX100 += Number(row.cost_cents_x100 ?? 0);\n prev.calls += 1;\n dayMap.set(date, prev);\n }\n const byDay: DailyBucket[] = [...dayMap.entries()]\n .map(([date, v]) => {\n const knownCents = v.knownCentsX100 / 100;\n return {\n date,\n cents: knownCents,\n cost: buildUsageCostAggregate(knownCents, v.unavailableCalls),\n calls: v.calls,\n };\n })\n .sort((a, b) => a.date.localeCompare(b.date));\n\n const recentRows = await client.execute({\n sql: `SELECT id, created_at, label, app, model,\n input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,\n cost_cents_x100, cost_source\n FROM token_usage\n WHERE owner_email = ?\n ORDER BY created_at DESC\n LIMIT 50`,\n args: [options.ownerEmail],\n });\n const recent: UsageRecentEntry[] = (\n recentRows.rows as Array<Record<string, number | string | null>>\n ).map((row) => ({\n id: Number(row.id),\n createdAt: Number(row.created_at),\n label: String(row.label ?? \"chat\"),\n app: String(row.app ?? \"\"),\n model: String(row.model ?? \"\"),\n inputTokens: Number(row.input_tokens ?? 0),\n outputTokens: Number(row.output_tokens ?? 0),\n cacheReadTokens: Number(row.cache_read_tokens ?? 0),\n cacheWriteTokens: Number(row.cache_write_tokens ?? 0),\n cents: Number(row.cost_cents_x100 ?? 0) / 100,\n costSource: String(row.cost_source ?? \"estimated\") as UsageCostSource,\n }));\n\n const knownCents = Number(t.known_cents ?? 0) / 100;\n const unavailableCalls = Number(t.unavailable_calls ?? 0);\n return {\n billing: USD_USAGE_BILLING,\n totalCents: knownCents,\n totalCost: buildUsageCostAggregate(knownCents, unavailableCalls),\n totalCalls: Number(t.calls ?? 0),\n totalInputTokens: Number(t.in_tok ?? 0),\n totalOutputTokens: Number(t.out_tok ?? 0),\n totalCacheReadTokens: Number(t.cr_tok ?? 0),\n totalCacheWriteTokens: Number(t.cw_tok ?? 0),\n sinceMs,\n byLabel: mapBuckets(byLabelR.rows),\n byModel: mapBuckets(byModelR.rows),\n byApp: mapBuckets(byAppR.rows),\n byDay,\n recent,\n };\n}\n\nfunction buildUsageCostAggregate(\n knownCents: number,\n unavailableCalls: number,\n): UsageCostAggregate {\n if (unavailableCalls === 0) {\n return { status: \"known\", knownCents, unavailableCalls: 0 };\n }\n if (knownCents === 0) {\n return { status: \"unavailable\", knownCents: 0, unavailableCalls };\n }\n return { status: \"partial\", knownCents, unavailableCalls };\n}\n"]}
@@ -364,6 +364,19 @@ pnpm dev
364
364
  طبقات DOM/الكود داخل ملف واحد معروض. تعكس لوحة الطبقات هذا التقسيم: الشاشات
365
365
  إطارات من المستوى الأعلى، وتحت الشاشة النشطة تظهر طبقات DOM/الكود المتداخلة.
366
366
 
367
+ ### Tweaks
368
+
369
+ Tweaks هي عناصر تحكم مسماة لتغيير أهم الخصائص المرئية في التصميم بسرعة ومن دون
370
+ تحرير المصدر. يمكن أن يوفر التصميم عينات ألوان وخيارات مقسمة وشرائط تمرير ومفاتيح
371
+ لخصائص مثل لون التمييز ونصف قطر الزوايا والتباعد والكثافة والرؤية. يرتبط كل عنصر
372
+ تحكم بخاصية CSS مخصصة يستخدمها التصميم المعروض، لذلك تتحدث المعاينة أثناء التعديل.
373
+
374
+ تتبع Tweaks تسلسل المحرر. ترث نقاط التوقف المتجاوبة والحالات القيمة المحددة في
375
+ التصميم الأساسي. يؤدي تغيير tweak أثناء تنشيط نقطة توقف أو حالة إلى إنشاء تجاوز
376
+ لهذا السياق؛ وتؤدي إعادة ضبط عنصر التحكم إلى إزالة التجاوز واستعادة القيمة
377
+ الموروثة. تبقى القيم المحفوظة مع التصميم. استخدم **إضافة الضوابط القرص** أو اطلب
378
+ من الوكيل إظهار خاصية مرئية أخرى عندما لا تتضمن اللوحة عنصر التحكم المطلوب.
379
+
367
380
  ثبّت `/visual-edit` عندما يحتاج وكيل البرمجة إلى فتح تطبيق محلي يعمل في
368
381
  Design بدل إنشاء HTML نموذج أولي مستقل:
369
382
 
@@ -365,6 +365,24 @@ und Bearbeitung der DOM-/Code-Ebenen in einer gerenderten Datei gedacht. Das
365
365
  Ebenenfenster spiegelt diese Trennung wider: Bildschirme sind Rahmen der
366
366
  obersten Ebene, darunter liegen die DOM-/Code-Ebenen des aktiven Bildschirms.
367
367
 
368
+ ### Tweaks
369
+
370
+ Tweaks sind benannte Steuerelemente, mit denen sich die wichtigsten visuellen
371
+ Eigenschaften eines Designs schnell und ohne Quellcodeänderung anpassen lassen.
372
+ Ein Design kann Farbfelder, segmentierte Optionen, Schieberegler und Schalter
373
+ für Akzentfarbe, Eckenradius, Abstände, Dichte oder Sichtbarkeit anbieten. Jedes
374
+ Steuerelement ist mit einer vom gerenderten Design verwendeten
375
+ CSS-Benutzereigenschaft verbunden, sodass die Vorschau beim Anpassen aktualisiert
376
+ wird.
377
+
378
+ Tweaks folgen der Kaskade des Editors. Responsive Breakpoints und Zustände erben
379
+ den im Basisdesign festgelegten Wert. Wird ein Tweak bei aktivem Breakpoint oder
380
+ Zustand geändert, entsteht eine Überschreibung für diesen Kontext; das Zurücksetzen
381
+ entfernt sie und stellt den geerbten Wert wieder her. Gespeicherte Werte bleiben
382
+ beim Design. Verwenden Sie **Fügen Sie Optimierungssteuerelemente hinzu** oder
383
+ bitten Sie den Agenten, eine weitere visuelle Eigenschaft bereitzustellen, wenn
384
+ das benötigte Steuerelement fehlt.
385
+
368
386
  Installiere `/visual-edit`, wenn ein Coding Agent eine laufende lokale App in
369
387
  Design öffnen soll, statt eigenständiges Prototype-HTML zu generieren:
370
388
 
@@ -364,6 +364,22 @@ comportamiento del prototipo y editar las capas DOM/código dentro de un archivo
364
364
  renderizado. El panel de capas refleja esa división: las pantallas son marcos de
365
365
  nivel superior, con capas DOM/código anidadas para la pantalla activa.
366
366
 
367
+ ### Tweaks
368
+
369
+ Los tweaks son controles con nombre para cambiar rápidamente las propiedades visuales
370
+ más útiles de un diseño sin editar su código fuente. Un diseño puede ofrecer muestras
371
+ de color, opciones segmentadas, deslizadores e interruptores para propiedades como el
372
+ color de acento, el radio de las esquinas, el espaciado, la densidad o la visibilidad.
373
+ Cada control está conectado a una propiedad CSS personalizada utilizada por el diseño,
374
+ por lo que la vista previa se actualiza al ajustarlo.
375
+
376
+ Los tweaks siguen la cascada del editor. Los puntos de quiebre y estados heredan el
377
+ valor definido en el diseño base. Cambiar un tweak mientras un punto de quiebre o
378
+ estado está activo crea una modificación para ese contexto; restablecer el control
379
+ elimina la modificación y recupera el valor heredado. Los valores guardados permanecen
380
+ con el diseño. Usa **Agregar controles de ajuste** o pide al agente que exponga otra
381
+ propiedad visual cuando falte el control que necesitas.
382
+
367
383
  Instala `/visual-edit` cuando un agente de código deba abrir una app local en
368
384
  ejecución dentro de Design en lugar de generar HTML de prototipo independiente:
369
385
 
@@ -364,6 +364,23 @@ prototype et modifier les couches DOM/code dans un fichier rendu. Le panneau des
364
364
  couches reflète cette séparation : les écrans sont des cadres de niveau
365
365
  supérieur, avec les couches DOM/code imbriquées pour l'écran actif.
366
366
 
367
+ ### Tweaks
368
+
369
+ Les tweaks sont des contrôles nommés qui permettent de modifier rapidement les
370
+ propriétés visuelles les plus utiles d'un design sans éditer sa source. Un design
371
+ peut proposer des nuanciers, des choix segmentés, des curseurs et des interrupteurs
372
+ pour la couleur d'accent, le rayon des angles, l'espacement, la densité ou la
373
+ visibilité. Chaque contrôle est relié à une propriété CSS personnalisée utilisée
374
+ par le design rendu, de sorte que l'aperçu se met à jour pendant le réglage.
375
+
376
+ Les tweaks suivent la cascade de l'éditeur. Les points d'arrêt responsive et les
377
+ états héritent de la valeur définie sur le design de base. Modifier un tweak pendant
378
+ qu'un point d'arrêt ou un état est actif crée une surcharge pour ce contexte ;
379
+ réinitialiser le contrôle supprime la surcharge et rétablit la valeur héritée. Les
380
+ valeurs enregistrées restent avec le design. Utilisez **Ajouter des contrôles de
381
+ réglage** ou demandez à l'agent d'exposer une autre propriété visuelle si le contrôle
382
+ nécessaire manque.
383
+
367
384
  Installez `/visual-edit` lorsqu'un agent de code doit ouvrir une app locale en
368
385
  cours d'exécution dans Design au lieu de générer du HTML de prototype autonome :
369
386
 
@@ -363,6 +363,22 @@ rendered file के अंदर DOM/code layers edit करने के ल
363
363
  model दिखाता है: screens top-level frames हैं, और active screen के नीचे nested
364
364
  DOM/code layers होते हैं।
365
365
 
366
+ ### Tweaks
367
+
368
+ Tweaks नाम वाले controls हैं जिनसे source edit किए बिना design की सबसे उपयोगी
369
+ visual properties जल्दी बदली जा सकती हैं। Design accent color, corner radius,
370
+ spacing, density या visibility जैसी properties के लिए color swatches, segmented
371
+ choices, sliders और toggles दे सकता है। हर control rendered design में उपयोग होने
372
+ वाली CSS custom property से जुड़ा होता है, इसलिए adjustment के साथ preview update
373
+ होता है।
374
+
375
+ Tweaks editor के cascade का पालन करते हैं। Base design में set value responsive
376
+ breakpoints और states को inherit होती है। किसी breakpoint या state के active होने
377
+ पर tweak बदलने से उस context के लिए override बनता है; control reset करने पर override
378
+ हट जाता है और inherited value वापस आती है। Saved values design के साथ रहते हैं।
379
+ ज़रूरी control न हो तो **ट्विक नियंत्रण जोड़ें** का उपयोग करें या agent से कोई दूसरी
380
+ visual property expose करने को कहें।
381
+
366
382
  जब coding agent को standalone prototype HTML बनाने के बजाय running local app को
367
383
  Design में खोलना हो, तब `/visual-edit` install करें:
368
384
 
@@ -364,6 +364,23 @@ Design は複数画面の作業では概要を最初に使います。生成後
364
364
  イヤーパネルもこの分離を反映します。画面は最上位フレームで、アクティブ
365
365
  画面の下に DOM/コードレイヤーがネストされます。
366
366
 
367
+ ### Tweaks
368
+
369
+ Tweaks は、ソースを編集せずにデザインの重要な視覚プロパティをすばやく
370
+ 変更するための名前付きコントロールです。デザインは、アクセントカラー、
371
+ 角丸、間隔、密度、表示状態などについて、カラースウォッチ、セグメント選択、
372
+ スライダー、トグルを提供できます。各コントロールはレンダリングされた
373
+ デザインが使用する CSS カスタムプロパティに接続され、調整と同時に
374
+ プレビューが更新されます。
375
+
376
+ Tweaks はエディターのカスケードに従います。ベースデザインで設定した値は、
377
+ レスポンシブブレークポイントと状態に継承されます。ブレークポイントまたは
378
+ 状態がアクティブなときに tweak を変更すると、そのコンテキスト用の
379
+ オーバーライドが作成されます。リセットするとオーバーライドが削除され、
380
+ 継承値に戻ります。保存した値はデザインとともに保持されます。必要な
381
+ コントロールがない場合は、**微調整コントロールを追加する**を使用するか、
382
+ 別の視覚プロパティを公開するようエージェントに依頼してください。
383
+
367
384
  コーディングエージェントがスタンドアロンのプロトタイプ HTML を生成する
368
385
  代わりに、実行中のローカルアプリを Design で開く必要がある場合は
369
386
  `/visual-edit` をインストールします。
@@ -363,6 +363,21 @@ Single-screen mode는 스크롤, prototype behavior와의 상호작용, 하나
363
363
  반영합니다. screens는 top-level frames이고, active screen 아래에 nested
364
364
  DOM/code layers가 표시됩니다.
365
365
 
366
+ ### Tweaks
367
+
368
+ Tweaks는 소스를 편집하지 않고 디자인에서 가장 유용한 시각 속성을 빠르게
369
+ 바꾸는 이름 있는 컨트롤입니다. 디자인은 강조색, 모서리 반경, 간격, 밀도,
370
+ 표시 여부 등에 대해 색상 견본, 분할 선택, 슬라이더, 토글을 제공할 수 있습니다.
371
+ 각 컨트롤은 렌더링된 디자인이 사용하는 CSS 사용자 지정 속성에 연결되므로
372
+ 조정하는 즉시 미리보기가 업데이트됩니다.
373
+
374
+ Tweaks는 편집기의 계단식 규칙을 따릅니다. 기본 디자인에서 설정한 값은 반응형
375
+ 중단점과 상태에 상속됩니다. 중단점이나 상태가 활성화된 동안 tweak를 변경하면
376
+ 해당 컨텍스트에 대한 재정의가 만들어지고, 컨트롤을 재설정하면 재정의가
377
+ 제거되어 상속된 값으로 돌아갑니다. 저장된 값은 디자인과 함께 유지됩니다.
378
+ 필요한 컨트롤이 없다면 **조정 컨트롤 추가**를 사용하거나 에이전트에게 다른
379
+ 시각 속성을 노출해 달라고 요청하세요.
380
+
366
381
  코딩 에이전트가 독립형 prototype HTML을 생성하는 대신 실행 중인 로컬 앱을
367
382
  Design에서 열어야 할 때 `/visual-edit`를 설치하세요:
368
383
 
@@ -364,6 +364,22 @@ protótipo e editar as camadas DOM/código dentro de um arquivo renderizado. O
364
364
  painel de camadas reflete essa divisão: telas são frames de nível superior, com
365
365
  camadas DOM/código aninhadas para a tela ativa.
366
366
 
367
+ ### Tweaks
368
+
369
+ Tweaks são controles nomeados para alterar rapidamente as propriedades visuais
370
+ mais úteis de um design sem editar o código-fonte. Um design pode oferecer
371
+ amostras de cor, opções segmentadas, controles deslizantes e alternâncias para
372
+ propriedades como cor de destaque, raio dos cantos, espaçamento, densidade ou
373
+ visibilidade. Cada controle se conecta a uma propriedade CSS personalizada usada
374
+ pelo design renderizado, então a visualização é atualizada durante o ajuste.
375
+
376
+ Tweaks seguem a cascata do editor. Breakpoints responsivos e estados herdam o
377
+ valor definido no design base. Alterar um tweak enquanto um breakpoint ou estado
378
+ está ativo cria uma substituição para esse contexto; redefinir o controle remove
379
+ a substituição e restaura o valor herdado. Os valores salvos permanecem com o
380
+ design. Use **Adicionar controles de ajuste** ou peça ao agente para expor outra
381
+ propriedade visual quando o controle necessário não estiver no painel.
382
+
367
383
  Instale `/visual-edit` quando um agente de código precisar abrir um app local em
368
384
  execução no Design em vez de gerar HTML de protótipo independente:
369
385
 
@@ -360,6 +360,17 @@ Design 对多屏工作采用概览优先。生成或进行大范围更新后,
360
360
  单屏模式用于滚动、与原型行为交互,以及编辑一个已渲染文件内的 DOM/代码
361
361
  层。图层面板也反映这种分工:屏幕是顶层框架,活动屏幕下嵌套 DOM/代码层。
362
362
 
363
+ ### Tweaks
364
+
365
+ Tweaks 是具名控件,无需编辑源代码即可快速调整设计中最常用的视觉属性。设计
366
+ 可以为强调色、圆角、间距、密度或可见性等属性提供色板、分段选项、滑块和开关。
367
+ 每个控件都连接到渲染设计使用的 CSS 自定义属性,因此调整时预览会立即更新。
368
+
369
+ Tweaks 遵循编辑器的级联规则。基础设计中设置的值会由响应式断点和状态继承。
370
+ 在某个断点或状态激活时更改 tweak,会为该上下文创建覆盖;重置控件会移除覆盖
371
+ 并恢复继承值。保存的 tweak 值会随设计保留。如果面板没有所需控件,请使用
372
+ **添加调整控件**,或让代理公开另一个视觉属性。
373
+
363
374
  当编码代理需要在 Design 中打开正在运行的本地应用,而不是生成独立原型 HTML
364
375
  时,请安装 `/visual-edit`:
365
376
 
@@ -361,6 +361,18 @@ Design 對多畫面工作採用概覽優先。產生或進行大範圍更新後
361
361
  式碼圖層。圖層面板也反映這種分工:畫面是頂層框架,作用中畫面下嵌套
362
362
  DOM/程式碼圖層。
363
363
 
364
+ ### Tweaks
365
+
366
+ Tweaks 是具名控制項,不必編輯原始碼即可快速調整設計中最實用的視覺屬性。
367
+ 設計可以為強調色、圓角、間距、密度或可見性等屬性提供色票、分段選項、滑桿
368
+ 和開關。每個控制項都連接到渲染設計使用的 CSS 自訂屬性,因此調整時預覽會
369
+ 立即更新。
370
+
371
+ Tweaks 遵循編輯器的級聯規則。基礎設計中設定的值會由響應式中斷點和狀態繼
372
+ 承。在某個中斷點或狀態作用時變更 tweak,會為該情境建立覆寫;重設控制項會
373
+ 移除覆寫並恢復繼承值。儲存的 tweak 值會隨設計保留。如果面板沒有需要的控制
374
+ 項,請使用 **新增調整控制項**,或要求代理公開另一個視覺屬性。
375
+
364
376
  當編碼代理需要在 Design 中開啟正在執行的本機應用,而不是產生獨立原型 HTML
365
377
  時,請安裝 `/visual-edit`:
366
378