@aglyn/plugins-ai 1.0.0-beta.225 → 1.0.0-beta.227

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 (161) hide show
  1. package/README.md +2 -1
  2. package/package.json +17 -17
  3. package/src/lib/billing/ai-month-meter.js +5 -3
  4. package/src/lib/billing/ai-month-meter.js.map +1 -1
  5. package/src/lib/billing/ai-overage-ledger.js +3 -1
  6. package/src/lib/billing/ai-overage-ledger.js.map +1 -1
  7. package/src/lib/components/ai-brief-dialog.component.d.ts +6 -2
  8. package/src/lib/components/ai-brief-dialog.component.js +35 -17
  9. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  10. package/src/lib/components/ai-describe-automation.component.js +17 -2
  11. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  12. package/src/lib/components/ai-insight-dialog.component.js +24 -2
  13. package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
  14. package/src/lib/components/ai-job-events.d.ts +14 -0
  15. package/src/lib/components/ai-job-events.js +76 -0
  16. package/src/lib/components/ai-job-events.js.map +1 -0
  17. package/src/lib/components/ai-job-follow.component.d.ts +49 -0
  18. package/src/lib/components/ai-job-follow.component.js +210 -0
  19. package/src/lib/components/ai-job-follow.component.js.map +1 -0
  20. package/src/lib/components/ai-job-links.d.ts +42 -0
  21. package/src/lib/components/ai-job-links.js +140 -0
  22. package/src/lib/components/ai-job-links.js.map +1 -0
  23. package/src/lib/components/ai-job-plan.component.d.ts +13 -6
  24. package/src/lib/components/ai-job-plan.component.js +29 -3
  25. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  26. package/src/lib/components/ai-job-requests.d.ts +24 -0
  27. package/src/lib/components/ai-job-requests.js +76 -0
  28. package/src/lib/components/ai-job-requests.js.map +1 -0
  29. package/src/lib/components/ai-jobs-indicator.component.d.ts +25 -0
  30. package/src/lib/components/ai-jobs-indicator.component.js +80 -0
  31. package/src/lib/components/ai-jobs-indicator.component.js.map +1 -0
  32. package/src/lib/components/ai-jobs-store.d.ts +81 -0
  33. package/src/lib/components/ai-jobs-store.js +218 -0
  34. package/src/lib/components/ai-jobs-store.js.map +1 -0
  35. package/src/lib/components/ai-page-brief-dialog.component.d.ts +3 -3
  36. package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -1
  37. package/src/lib/components/ai-site-build-page.component.d.ts +23 -0
  38. package/src/lib/components/ai-site-build-page.component.js +377 -0
  39. package/src/lib/components/ai-site-build-page.component.js.map +1 -0
  40. package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
  41. package/src/lib/components/ai-site-start-card.component.js +522 -130
  42. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  43. package/src/lib/components/ai-site-starter-fallback.component.d.ts +24 -0
  44. package/src/lib/components/ai-site-starter-fallback.component.js +112 -0
  45. package/src/lib/components/ai-site-starter-fallback.component.js.map +1 -0
  46. package/src/lib/components/assist-jobs-drawer.component.d.ts +18 -20
  47. package/src/lib/components/assist-jobs-drawer.component.js +152 -172
  48. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  49. package/src/lib/components/assist-panel.component.js +62 -6
  50. package/src/lib/components/assist-panel.component.js.map +1 -1
  51. package/src/lib/components/staff-ai-credit-return.component.d.ts +55 -0
  52. package/src/lib/components/staff-ai-credit-return.component.js +346 -0
  53. package/src/lib/components/staff-ai-credit-return.component.js.map +1 -0
  54. package/src/lib/components/staff-org-ai-card.component.js +364 -279
  55. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  56. package/src/lib/components/staff-user-ai-usage-card.component.d.ts +4 -0
  57. package/src/lib/components/staff-user-ai-usage-card.component.js +83 -28
  58. package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -1
  59. package/src/lib/components/use-ai-job-run.js +5 -18
  60. package/src/lib/components/use-ai-job-run.js.map +1 -1
  61. package/src/lib/jobs/ai-job-auto-confirm.d.ts +52 -0
  62. package/src/lib/jobs/ai-job-auto-confirm.js +62 -0
  63. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -0
  64. package/src/lib/jobs/ai-job-plan-step.d.ts +33 -2
  65. package/src/lib/jobs/ai-job-plan-step.js +161 -17
  66. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  67. package/src/lib/jobs/ai-job-site-step.d.ts +7 -0
  68. package/src/lib/jobs/ai-job-site-step.js +34 -4
  69. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  70. package/src/lib/jobs/ai-job-text-step.d.ts +7 -0
  71. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  72. package/src/lib/jobs/ai-jobs-notify.d.ts +37 -0
  73. package/src/lib/jobs/ai-jobs-notify.js +51 -0
  74. package/src/lib/jobs/ai-jobs-notify.js.map +1 -0
  75. package/src/lib/jobs/ai-jobs.d.ts +31 -4
  76. package/src/lib/jobs/ai-jobs.js +196 -32
  77. package/src/lib/jobs/ai-jobs.js.map +1 -1
  78. package/src/lib/model/ai-job-activity.d.ts +112 -0
  79. package/src/lib/model/ai-job-activity.js +144 -0
  80. package/src/lib/model/ai-job-activity.js.map +1 -0
  81. package/src/lib/model/ai-job-failure-copy.d.ts +69 -0
  82. package/src/lib/model/ai-job-failure-copy.js +78 -0
  83. package/src/lib/model/ai-job-failure-copy.js.map +1 -0
  84. package/src/lib/model/ai-job-notice.d.ts +61 -0
  85. package/src/lib/model/ai-job-notice.js +112 -0
  86. package/src/lib/model/ai-job-notice.js.map +1 -0
  87. package/src/lib/model/ai-jobs.types.d.ts +26 -0
  88. package/src/lib/model/ai-jobs.types.js +1 -0
  89. package/src/lib/model/ai-jobs.types.js.map +1 -1
  90. package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
  91. package/src/lib/model/ai-plan-capabilities.js.map +1 -1
  92. package/src/lib/model/ai-site-build-progress.d.ts +45 -0
  93. package/src/lib/model/ai-site-build-progress.js +76 -0
  94. package/src/lib/model/ai-site-build-progress.js.map +1 -0
  95. package/src/lib/model/ai-site-inventory.d.ts +9 -0
  96. package/src/lib/model/ai-site-inventory.js +7 -2
  97. package/src/lib/model/ai-site-inventory.js.map +1 -1
  98. package/src/lib/model/ai-site-job.d.ts +93 -2
  99. package/src/lib/model/ai-site-job.js +88 -7
  100. package/src/lib/model/ai-site-job.js.map +1 -1
  101. package/src/lib/model/ai-site-start.d.ts +3 -1
  102. package/src/lib/model/ai-site-start.js +6 -4
  103. package/src/lib/model/ai-site-start.js.map +1 -1
  104. package/src/lib/model/ai-template-subjects.d.ts +4 -3
  105. package/src/lib/model/ai-template-subjects.js +5 -4
  106. package/src/lib/model/ai-template-subjects.js.map +1 -1
  107. package/src/lib/plan-entitlements.d.ts +3 -3
  108. package/src/lib/plan-entitlements.js +3 -3
  109. package/src/lib/plan-entitlements.js.map +1 -1
  110. package/src/lib/plugin.js +28 -0
  111. package/src/lib/plugin.js.map +1 -1
  112. package/src/lib/runtime/ai-abuse-guards.d.ts +9 -6
  113. package/src/lib/runtime/ai-abuse-guards.js +10 -7
  114. package/src/lib/runtime/ai-abuse-guards.js.map +1 -1
  115. package/src/lib/runtime/ai-doctrine-validators.js +31 -4
  116. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  117. package/src/lib/runtime/ai-gate.d.ts +3 -2
  118. package/src/lib/runtime/ai-gate.js +3 -2
  119. package/src/lib/runtime/ai-gate.js.map +1 -1
  120. package/src/lib/runtime/ai-palette.generated.js +12 -4
  121. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  122. package/src/lib/runtime/site-inventory.d.ts +16 -1
  123. package/src/lib/runtime/site-inventory.js +43 -3
  124. package/src/lib/runtime/site-inventory.js.map +1 -1
  125. package/src/lib/server/ai-admin-credits.d.ts +21 -0
  126. package/src/lib/server/ai-admin-credits.js +213 -0
  127. package/src/lib/server/ai-admin-credits.js.map +1 -0
  128. package/src/lib/server/ai-admin-org.js +12 -5
  129. package/src/lib/server/ai-admin-org.js.map +1 -1
  130. package/src/lib/server/ai-admin-signals.js +3 -1
  131. package/src/lib/server/ai-admin-signals.js.map +1 -1
  132. package/src/lib/server/ai-admin-user.js +12 -1
  133. package/src/lib/server/ai-admin-user.js.map +1 -1
  134. package/src/lib/server/ai-allotments.js +3 -2
  135. package/src/lib/server/ai-allotments.js.map +1 -1
  136. package/src/lib/server/ai-jobs-route.js +20 -2
  137. package/src/lib/server/ai-jobs-route.js.map +1 -1
  138. package/src/lib/server/assist-chat.js +3 -2
  139. package/src/lib/server/assist-chat.js.map +1 -1
  140. package/src/lib/server/billing-credits.js +4 -3
  141. package/src/lib/server/billing-credits.js.map +1 -1
  142. package/src/lib/server/billing-overage.js +3 -2
  143. package/src/lib/server/billing-overage.js.map +1 -1
  144. package/src/lib/server.js +11 -0
  145. package/src/lib/server.js.map +1 -1
  146. package/src/lib/usage/assist-credit-returns-write.d.ts +75 -0
  147. package/src/lib/usage/assist-credit-returns-write.js +150 -0
  148. package/src/lib/usage/assist-credit-returns-write.js.map +1 -0
  149. package/src/lib/usage/assist-credit-returns.d.ts +68 -0
  150. package/src/lib/usage/assist-credit-returns.js +79 -0
  151. package/src/lib/usage/assist-credit-returns.js.map +1 -0
  152. package/src/lib/usage/assist-free-taste.js +6 -3
  153. package/src/lib/usage/assist-free-taste.js.map +1 -1
  154. package/src/lib/usage/assist-job-refund.d.ts +100 -0
  155. package/src/lib/usage/assist-job-refund.js +109 -0
  156. package/src/lib/usage/assist-job-refund.js.map +1 -0
  157. package/src/lib/usage/assist-usage.js +5 -2
  158. package/src/lib/usage/assist-usage.js.map +1 -1
  159. package/src/lib/usage/staff-org-ai.d.ts +31 -1
  160. package/src/lib/usage/staff-org-ai.js +19 -2
  161. package/src/lib/usage/staff-org-ai.js.map +1 -1
@@ -27,7 +27,8 @@ import { useUser } from "@aglyn/tenant-feature-instance";
27
27
  import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
28
28
  import { Alert, Chip, Stack, TableBody, TableCell, TableHead, TableRow, Typography } from "@mui/material";
29
29
  import { useRouter } from "next/navigation";
30
- import { useEffect, useMemo, useRef, useState } from "react";
30
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
31
+ import { StaffCreditReturnActions, StaffCreditReturnDialog } from "./staff-ai-credit-return.component.js";
31
32
  /**
32
33
  * THE STAFF AI CARD (AGL-2930): everything about one org's AI usage on the
33
34
  * page staff already open to look at that org.
@@ -52,6 +53,11 @@ import { useEffect, useMemo, useRef, useState } from "react";
52
53
  * sends and generates, and how much of its prompt the cache served.
53
54
  * - **Margin** — spend against what AI brings in, red when it is over.
54
55
  * - **Actions** — where the override editor and the AI pause live.
56
+ *
57
+ * The header carries the card's two acts (AGL-3595): give credits back to
58
+ * the band (and, on a Free workspace, to the owner's allowance), or reset
59
+ * the month — and each recent job offers the same give-back, prefilled with
60
+ * that job's spend.
55
61
  */ const credits = (value)=>value == null ? '—' : Math.round(value).toLocaleString();
56
62
  /**
57
63
  * Four decimals under a dollar, two above (the assist-signals page's rule):
@@ -131,7 +137,10 @@ function RefusalsRow({ refusals }) {
131
137
  })
132
138
  });
133
139
  }
134
- function JobsSection({ jobs }) {
140
+ function JobsSection({ jobs, onGiveBack }) {
141
+ const columns = useMemo(()=>jobColumns(onGiveBack), [
142
+ onGiveBack
143
+ ]);
135
144
  if (jobs === null) {
136
145
  return /*#__PURE__*/ _jsx(Alert, {
137
146
  severity: "warning",
@@ -182,7 +191,7 @@ function JobsSection({ jobs }) {
182
191
  }) : null,
183
192
  /*#__PURE__*/ _jsx(ListTable, {
184
193
  rows: jobs.recent,
185
- columns: JOB_COLUMNS,
194
+ columns: columns,
186
195
  getRowId: (job)=>job.id,
187
196
  rowHeight: TABLE_ROW_HEIGHT,
188
197
  noRowsLabel: "No recent jobs",
@@ -198,69 +207,81 @@ function JobsSection({ jobs }) {
198
207
  * The recent jobs, one column per fact. Who started a job is a column of
199
208
  * its own rather than a link tucked beside the date, so it sorts like the
200
209
  * rest; the credits sort by what was SPENT, which is the figure the
201
- * pool was charged.
202
- */ const JOB_COLUMNS = [
203
- {
204
- field: 'id',
205
- headerName: 'Job',
206
- flex: 1,
207
- minWidth: 200,
208
- renderCell: ({ row })=>/*#__PURE__*/ _jsx(Typography, {
209
- variant: "body2",
210
- noWrap: true,
211
- title: row.id,
212
- sx: {
213
- fontFamily: 'monospace'
214
- },
215
- children: row.id
216
- })
217
- },
218
- {
219
- field: 'kind',
220
- headerName: 'Kind',
221
- width: 120,
222
- valueFormatter: (value)=>value || '—'
223
- },
224
- {
225
- field: 'status',
226
- headerName: 'Status',
227
- width: 130,
228
- valueFormatter: (value)=>String(value != null ? value : '').replace('_', ' ')
229
- },
230
- {
231
- field: 'creditsSpent',
232
- headerName: 'Credits',
233
- type: 'number',
234
- align: 'right',
235
- headerAlign: 'right',
236
- width: 140,
237
- valueFormatter: (value, row)=>`${credits(value)} / ${credits(row.creditsReserved)}`
238
- },
239
- {
240
- field: 'createdAt',
241
- headerName: 'Started',
242
- width: 120,
243
- valueFormatter: (value)=>when(value)
244
- },
245
- {
246
- field: 'createdBy',
247
- headerName: 'Started by',
248
- flex: 1,
249
- minWidth: 240,
250
- renderCell: ({ row })=>row.createdBy ? /*#__PURE__*/ _jsx(AppLink, {
251
- href: buildRoute(Route.ADMIN_USER_DETAIL, {
252
- uid: row.createdBy
253
- }),
254
- variant: "body2",
255
- underline: "hover",
256
- noWrap: true,
257
- sx: {
258
- fontFamily: 'monospace'
259
- },
260
- children: row.createdBy
261
- }) : '—'
262
- }
263
- ];
210
+ * pool was charged. The row's action gives that spend back (AGL-3595).
211
+ */ const jobColumns = (onGiveBack)=>[
212
+ {
213
+ field: 'id',
214
+ headerName: 'Job',
215
+ flex: 1,
216
+ minWidth: 200,
217
+ renderCell: ({ row })=>/*#__PURE__*/ _jsx(Typography, {
218
+ variant: "body2",
219
+ noWrap: true,
220
+ title: row.id,
221
+ sx: {
222
+ fontFamily: 'monospace'
223
+ },
224
+ children: row.id
225
+ })
226
+ },
227
+ {
228
+ field: 'kind',
229
+ headerName: 'Kind',
230
+ width: 120,
231
+ valueFormatter: (value)=>value || '—'
232
+ },
233
+ {
234
+ field: 'status',
235
+ headerName: 'Status',
236
+ width: 130,
237
+ valueFormatter: (value)=>String(value != null ? value : '').replace('_', ' ')
238
+ },
239
+ {
240
+ field: 'creditsSpent',
241
+ headerName: 'Credits',
242
+ type: 'number',
243
+ align: 'right',
244
+ headerAlign: 'right',
245
+ width: 140,
246
+ valueFormatter: (value, row)=>`${credits(value)} / ${credits(row.creditsReserved)}`
247
+ },
248
+ {
249
+ field: 'createdAt',
250
+ headerName: 'Started',
251
+ width: 120,
252
+ valueFormatter: (value)=>when(value)
253
+ },
254
+ {
255
+ field: 'createdBy',
256
+ headerName: 'Started by',
257
+ flex: 1,
258
+ minWidth: 240,
259
+ renderCell: ({ row })=>row.createdBy ? /*#__PURE__*/ _jsx(AppLink, {
260
+ href: buildRoute(Route.ADMIN_USER_DETAIL, {
261
+ uid: row.createdBy
262
+ }),
263
+ variant: "body2",
264
+ underline: "hover",
265
+ noWrap: true,
266
+ sx: {
267
+ fontFamily: 'monospace'
268
+ },
269
+ children: row.createdBy
270
+ }) : '—'
271
+ },
272
+ listActionsColumn((row)=>/*#__PURE__*/ _jsx(ListRowActions, {
273
+ label: row.id,
274
+ items: [
275
+ {
276
+ key: 'give-back',
277
+ label: 'Give back this job’s credits',
278
+ disabled: row.creditsSpent <= 0,
279
+ disabledReason: row.creditsSpent <= 0 ? 'This job spent no credits.' : undefined,
280
+ onClick: ()=>onGiveBack(row)
281
+ }
282
+ ]
283
+ }))
284
+ ];
264
285
  const tokenCount = (value)=>Math.round(value).toLocaleString();
265
286
  const rate = (value)=>value == null ? '—' : `${Math.round(value * 100)}%`;
266
287
  /** The month's tokens, and what each kind of request spends of them (AGL-2937). */ function TokensSection({ tokens }) {
@@ -472,7 +493,7 @@ const rate = (value)=>value == null ? '—' : `${Math.round(value * 100)}%`;
472
493
  });
473
494
  }
474
495
  const StaffOrgAiCard = ({ orgId })=>{
475
- var _ref;
496
+ var _ref, _ref1, _ref2, _ref3;
476
497
  const { data: user } = useUser();
477
498
  // Keyed on who is signed in, not on the user object's identity.
478
499
  const signedInUid = (_ref = user == null ? void 0 : user.uid) != null ? _ref : null;
@@ -481,6 +502,15 @@ const StaffOrgAiCard = ({ orgId })=>{
481
502
  const [data, setData] = useState(null);
482
503
  const [ready, setReady] = useState(false);
483
504
  const [error, setError] = useState(null);
505
+ // Bumped after a give-back, so the card re-reads the figures it changed.
506
+ const [version, setVersion] = useState(0);
507
+ const [notice, setNotice] = useState(null);
508
+ const [returning, setReturning] = useState(null);
509
+ const giveBackJob = useCallback((job)=>setReturning({
510
+ mode: 'give',
511
+ jobId: job.id,
512
+ jobCredits: job.creditsSpent
513
+ }), []);
484
514
  useEffect(()=>{
485
515
  // Mounted only for a confirmed staff reader — the `staffOrg` zone sits
486
516
  // inside the page's `StaffOnly`, which renders nothing while the claim
@@ -518,227 +548,282 @@ const StaffOrgAiCard = ({ orgId })=>{
518
548
  };
519
549
  }, [
520
550
  orgId,
521
- signedInUid
551
+ signedInUid,
552
+ version
522
553
  ]);
523
554
  const name = aiAddonName();
524
- return /*#__PURE__*/ _jsx(CardDisplay, {
555
+ return /*#__PURE__*/ _jsxs(CardDisplay, {
525
556
  header: name,
526
557
  help: pluginDocsHelp('aiMonitoring', {
527
558
  anchor: '#the-ai-card',
528
559
  excerpt: 'The add-on, the credit pool and its parts, this month’s overage and refusals, generation jobs, tokens and the cache hit rate by kind, the people spending the most, and the margin — for this organization.'
529
560
  }),
561
+ HeaderProps: {
562
+ action: /*#__PURE__*/ _jsx(StaffCreditReturnActions, {
563
+ disabled: !data,
564
+ onGive: ()=>setReturning({
565
+ mode: 'give'
566
+ }),
567
+ onReset: ()=>setReturning({
568
+ mode: 'reset'
569
+ })
570
+ })
571
+ },
530
572
  contentGutterX: true,
531
573
  contentGutterY: true,
532
- children: !ready ? /*#__PURE__*/ _jsx(Typography, {
533
- variant: "body2",
534
- color: "text.secondary",
535
- children: 'Loading…'
536
- }) : error || !data ? /*#__PURE__*/ _jsx(Alert, {
537
- severity: "warning",
538
- children: `Could not read this organization’s AI usage — ${error != null ? error : 'a failed read'}, not zero usage.`
539
- }) : /*#__PURE__*/ _jsxs(Stack, {
540
- spacing: 2,
541
- children: [
542
- /*#__PURE__*/ _jsxs(Stack, {
543
- spacing: 0.5,
544
- children: [
545
- /*#__PURE__*/ _jsx(Typography, {
546
- variant: "overline",
547
- color: "text.secondary",
548
- children: 'Add-on'
549
- }),
550
- /*#__PURE__*/ _jsxs(Stack, {
551
- direction: "row",
552
- spacing: 1,
553
- sx: {
554
- alignItems: 'center',
555
- flexWrap: 'wrap'
556
- },
557
- children: [
558
- /*#__PURE__*/ _jsx(Chip, {
559
- size: "small",
560
- color: data.addon.on ? 'primary' : 'default',
561
- label: data.addon.on ? `${name} on` : `${name} off`
562
- }),
563
- data.addon.priceUsd !== null ? /*#__PURE__*/ _jsx(Typography, {
564
- variant: "body2",
565
- children: `${usd(data.addon.priceUsd)}/mo on this plan`
566
- }) : /*#__PURE__*/ _jsx(Typography, {
567
- variant: "body2",
568
- color: "text.secondary",
569
- children: 'Not sold on this plan'
570
- }),
571
- data.addon.on ? /*#__PURE__*/ _jsx(Typography, {
572
- variant: "body2",
573
- color: "text.secondary",
574
- children: data.addon.since ? `since ${when(data.addon.since)}` : data.addon.sinceSource === 'unavailable' ? 'since — (Stripe not reachable)' : 'since — (no subscription item)'
575
- }) : null
576
- ]
577
- })
578
- ]
579
- }),
580
- /*#__PURE__*/ _jsxs(Stack, {
581
- spacing: 0.5,
582
- children: [
583
- /*#__PURE__*/ _jsx(Typography, {
584
- variant: "overline",
585
- color: "text.secondary",
586
- children: `Credit pool · ${data.month}`
587
- }),
588
- data.pool.totalCredits === null && data.pool.uncapped ? /*#__PURE__*/ _jsx(Typography, {
589
- variant: "body2",
590
- color: "text.secondary",
591
- children: 'Uncapped staff comp — no AI credit band. Bounded only by the message cap and an operator’s explicit spend ceiling.'
592
- }) : data.pool.totalCredits === null ? /*#__PURE__*/ _jsx(Typography, {
593
- variant: "body2",
594
- color: "text.secondary",
595
- children: 'No AI credit band on this plan — bounded by the message cap and the operator backstop.'
596
- }) : /*#__PURE__*/ _jsx(Typography, {
597
- variant: "body2",
598
- children: `${credits(data.pool.totalCredits)} credits = ${poolPartsSentence(data.pool)}`
599
- }),
600
- /*#__PURE__*/ _jsxs(Typography, {
601
- variant: "body2",
602
- children: [
603
- `Used ${credits(data.pool.usedCredits)} credits (${usd(data.pool.providerUsd)} provider spend)`,
604
- data.pool.remainingCredits !== null ? ` · ${credits(data.pool.remainingCredits)} remaining` : ''
605
- ]
606
- }),
607
- /*#__PURE__*/ _jsx(Typography, {
608
- variant: "body2",
609
- color: "text.secondary",
610
- children: `Projected month-end at this pace: ${credits(data.pool.projectedCredits)} credits (${usd(data.pool.projectedUsd)}) · ${data.pool.messages.toLocaleString()} model turns, ${data.pool.deflected.toLocaleString()} answered from the docs`
611
- })
612
- ]
613
- }),
614
- /*#__PURE__*/ _jsxs(Stack, {
615
- spacing: 0.5,
616
- children: [
617
- /*#__PURE__*/ _jsx(Typography, {
618
- variant: "overline",
619
- color: "text.secondary",
620
- children: 'Overage'
621
- }),
622
- /*#__PURE__*/ _jsx(Typography, {
623
- variant: "body2",
624
- children: `${credits(data.overage.overageCredits)} credits over the band · ${usd(data.overage.accruedUsd)} accrued`
625
- }),
626
- /*#__PURE__*/ _jsx(Typography, {
627
- variant: "body2",
628
- color: "text.secondary",
629
- children: overageStateSentence(data.overage, data.pool.uncapped === true)
630
- })
631
- ]
632
- }),
633
- /*#__PURE__*/ _jsxs(Stack, {
634
- spacing: 0.5,
635
- children: [
636
- /*#__PURE__*/ _jsx(Typography, {
637
- variant: "overline",
638
- color: "text.secondary",
639
- children: `Refusals this month · ${data.refusals.total.toLocaleString()}`
640
- }),
641
- /*#__PURE__*/ _jsx(RefusalsRow, {
642
- refusals: data.refusals
643
- })
644
- ]
645
- }),
646
- /*#__PURE__*/ _jsxs(Stack, {
647
- spacing: 0.5,
648
- children: [
649
- /*#__PURE__*/ _jsx(Typography, {
650
- variant: "overline",
651
- color: "text.secondary",
652
- children: 'Generation jobs'
653
- }),
654
- /*#__PURE__*/ _jsx(JobsSection, {
655
- jobs: data.jobs
656
- })
657
- ]
658
- }),
659
- data.tokens ? /*#__PURE__*/ _jsxs(Stack, {
660
- spacing: 0.5,
661
- children: [
662
- /*#__PURE__*/ _jsx(Typography, {
663
- variant: "overline",
664
- color: "text.secondary",
665
- children: `Tokens · ${data.month}`
666
- }),
667
- /*#__PURE__*/ _jsx(TokensSection, {
668
- tokens: data.tokens
669
- })
670
- ]
671
- }) : null,
672
- /*#__PURE__*/ _jsxs(Stack, {
673
- spacing: 0.5,
674
- children: [
675
- /*#__PURE__*/ _jsx(Typography, {
676
- variant: "overline",
677
- color: "text.secondary",
678
- children: 'Top users this month'
679
- }),
680
- data.users.length === 0 ? /*#__PURE__*/ _jsx(Typography, {
681
- variant: "body2",
682
- color: "text.secondary",
683
- children: 'No AI usage attributed to a person this month.'
684
- }) : /*#__PURE__*/ _jsx(TopUsersSection, {
685
- users: data.users
686
- })
687
- ]
688
- }),
689
- /*#__PURE__*/ _jsxs(Stack, {
690
- spacing: 0.5,
691
- children: [
692
- /*#__PURE__*/ _jsx(Typography, {
693
- variant: "overline",
694
- color: "text.secondary",
695
- children: 'Margin'
696
- }),
697
- /*#__PURE__*/ _jsxs(Alert, {
698
- severity: marginTone(data.margin),
699
- children: [
700
- `Spend ${usd(data.margin.spendUsd)} against ${usd(data.margin.revenueUsd)} in — ` + `${usd(data.margin.addonRevenueUsd)} add-on, ${usd(data.margin.planAssistShareUsd)} plan assist share, ` + `${usd(data.margin.overageRevenueUsd)} overage.`,
701
- data.margin.underwater ? ' Spend exceeds the add-on plus the plan’s assist share.' : '',
702
- data.margin.multiple >= 1 ? ` ${data.margin.multiple}× the ${usd(data.margin.thresholdUsd)} staff review threshold.` : ''
703
- ]
704
- }),
705
- data.margin.contribution.marginPct !== null ? /*#__PURE__*/ _jsx(Typography, {
706
- variant: "caption",
707
- color: "text.secondary",
708
- children: `Whole-org contribution margin ${Math.round(data.margin.contribution.marginPct * 100)}% on the ${data.margin.contribution.month} rollup with this month’s AI spend.`
709
- }) : null
710
- ]
711
- }),
712
- /*#__PURE__*/ _jsxs(Stack, {
713
- spacing: 0.5,
714
- children: [
715
- /*#__PURE__*/ _jsx(Typography, {
716
- variant: "overline",
717
- color: "text.secondary",
718
- children: 'Actions'
719
- }),
720
- /*#__PURE__*/ _jsxs(Typography, {
721
- variant: "body2",
722
- children: [
723
- 'Credit override and feature toggles: ',
724
- /*#__PURE__*/ _jsx(AppLink, {
725
- href: buildRoute(Route.ADMIN_ORGS),
726
- underline: "hover",
727
- children: 'the entitlement override editor'
728
- }),
729
- ' (also under Staff actions above). Pause AI for this org: ',
730
- /*#__PURE__*/ _jsx(AppLink, {
731
- href: buildRoute(Route.ADMIN_LOCKDOWN),
732
- underline: "hover",
733
- children: 'Lockdown'
734
- }),
735
- ', feature keys ai-assist and ai-generate scoped to the org.'
736
- ]
737
- })
738
- ]
739
- })
740
- ]
741
- })
574
+ children: [
575
+ data ? /*#__PURE__*/ _jsx(StaffCreditReturnDialog, {
576
+ open: returning !== null,
577
+ mode: (_ref1 = returning == null ? void 0 : returning.mode) != null ? _ref1 : 'give',
578
+ target: {
579
+ orgId
580
+ },
581
+ workspace: {
582
+ used: data.pool.usedCredits,
583
+ limit: data.pool.totalCredits
584
+ },
585
+ account: data.account ? {
586
+ used: data.account.usedCredits,
587
+ limit: data.account.limitCredits
588
+ } : null,
589
+ jobId: (_ref2 = returning == null ? void 0 : returning.jobId) != null ? _ref2 : null,
590
+ jobCredits: (_ref3 = returning == null ? void 0 : returning.jobCredits) != null ? _ref3 : null,
591
+ onClose: ()=>setReturning(null),
592
+ onReturned: (message)=>{
593
+ setReturning(null);
594
+ setNotice(message);
595
+ setVersion((current)=>current + 1);
596
+ }
597
+ }) : null,
598
+ notice ? /*#__PURE__*/ _jsx(Alert, {
599
+ severity: "success",
600
+ onClose: ()=>setNotice(null),
601
+ sx: {
602
+ mb: 2
603
+ },
604
+ children: notice
605
+ }) : null,
606
+ !ready ? /*#__PURE__*/ _jsx(Typography, {
607
+ variant: "body2",
608
+ color: "text.secondary",
609
+ children: 'Loading…'
610
+ }) : error || !data ? /*#__PURE__*/ _jsx(Alert, {
611
+ severity: "warning",
612
+ children: `Could not read this organization’s AI usage — ${error != null ? error : 'a failed read'}, not zero usage.`
613
+ }) : /*#__PURE__*/ _jsxs(Stack, {
614
+ spacing: 2,
615
+ children: [
616
+ /*#__PURE__*/ _jsxs(Stack, {
617
+ spacing: 0.5,
618
+ children: [
619
+ /*#__PURE__*/ _jsx(Typography, {
620
+ variant: "overline",
621
+ color: "text.secondary",
622
+ children: 'Add-on'
623
+ }),
624
+ /*#__PURE__*/ _jsxs(Stack, {
625
+ direction: "row",
626
+ spacing: 1,
627
+ sx: {
628
+ alignItems: 'center',
629
+ flexWrap: 'wrap'
630
+ },
631
+ children: [
632
+ /*#__PURE__*/ _jsx(Chip, {
633
+ size: "small",
634
+ color: data.addon.on ? 'primary' : 'default',
635
+ label: data.addon.on ? `${name} on` : `${name} off`
636
+ }),
637
+ data.addon.priceUsd !== null ? /*#__PURE__*/ _jsx(Typography, {
638
+ variant: "body2",
639
+ children: `${usd(data.addon.priceUsd)}/mo on this plan`
640
+ }) : /*#__PURE__*/ _jsx(Typography, {
641
+ variant: "body2",
642
+ color: "text.secondary",
643
+ children: 'Not sold on this plan'
644
+ }),
645
+ data.addon.on ? /*#__PURE__*/ _jsx(Typography, {
646
+ variant: "body2",
647
+ color: "text.secondary",
648
+ children: data.addon.since ? `since ${when(data.addon.since)}` : data.addon.sinceSource === 'unavailable' ? 'since — (Stripe not reachable)' : 'since — (no subscription item)'
649
+ }) : null
650
+ ]
651
+ })
652
+ ]
653
+ }),
654
+ /*#__PURE__*/ _jsxs(Stack, {
655
+ spacing: 0.5,
656
+ children: [
657
+ /*#__PURE__*/ _jsx(Typography, {
658
+ variant: "overline",
659
+ color: "text.secondary",
660
+ children: `Credit pool · ${data.month}`
661
+ }),
662
+ data.pool.totalCredits === null && data.pool.uncapped ? /*#__PURE__*/ _jsx(Typography, {
663
+ variant: "body2",
664
+ color: "text.secondary",
665
+ children: 'Uncapped staff comp — no AI credit band. Bounded only by the message cap and an operator’s explicit spend ceiling.'
666
+ }) : data.pool.totalCredits === null ? /*#__PURE__*/ _jsx(Typography, {
667
+ variant: "body2",
668
+ color: "text.secondary",
669
+ children: 'No AI credit band on this plan — bounded by the message cap and the operator backstop.'
670
+ }) : /*#__PURE__*/ _jsx(Typography, {
671
+ variant: "body2",
672
+ children: `${credits(data.pool.totalCredits)} credits = ${poolPartsSentence(data.pool)}`
673
+ }),
674
+ /*#__PURE__*/ _jsxs(Typography, {
675
+ variant: "body2",
676
+ children: [
677
+ `Used ${credits(data.pool.usedCredits)} credits (${usd(data.pool.providerUsd)} provider spend)`,
678
+ data.pool.remainingCredits !== null ? ` · ${credits(data.pool.remainingCredits)} remaining` : ''
679
+ ]
680
+ }),
681
+ data.pool.returnedCredits ? /*#__PURE__*/ _jsx(Typography, {
682
+ variant: "body2",
683
+ color: "text.secondary",
684
+ children: `${credits(data.pool.returnedCredits)} credits given back this month — already taken off the figure above.`
685
+ }) : null,
686
+ data.account ? /*#__PURE__*/ _jsx(Typography, {
687
+ variant: "body2",
688
+ children: `Owner’s Free allowance: ${credits(data.account.usedCredits)} of ${credits(data.account.limitCredits)} used across their free workspaces` + (data.account.returnedCredits ? ` (${credits(data.account.returnedCredits)} given back)` : '')
689
+ }) : null,
690
+ /*#__PURE__*/ _jsx(Typography, {
691
+ variant: "body2",
692
+ color: "text.secondary",
693
+ children: `Projected month-end at this pace: ${credits(data.pool.projectedCredits)} credits (${usd(data.pool.projectedUsd)}) · ${data.pool.messages.toLocaleString()} model turns, ${data.pool.deflected.toLocaleString()} answered from the docs`
694
+ })
695
+ ]
696
+ }),
697
+ /*#__PURE__*/ _jsxs(Stack, {
698
+ spacing: 0.5,
699
+ children: [
700
+ /*#__PURE__*/ _jsx(Typography, {
701
+ variant: "overline",
702
+ color: "text.secondary",
703
+ children: 'Overage'
704
+ }),
705
+ /*#__PURE__*/ _jsx(Typography, {
706
+ variant: "body2",
707
+ children: `${credits(data.overage.overageCredits)} credits over the band · ${usd(data.overage.accruedUsd)} accrued`
708
+ }),
709
+ /*#__PURE__*/ _jsx(Typography, {
710
+ variant: "body2",
711
+ color: "text.secondary",
712
+ children: overageStateSentence(data.overage, data.pool.uncapped === true)
713
+ })
714
+ ]
715
+ }),
716
+ /*#__PURE__*/ _jsxs(Stack, {
717
+ spacing: 0.5,
718
+ children: [
719
+ /*#__PURE__*/ _jsx(Typography, {
720
+ variant: "overline",
721
+ color: "text.secondary",
722
+ children: `Refusals this month · ${data.refusals.total.toLocaleString()}`
723
+ }),
724
+ /*#__PURE__*/ _jsx(RefusalsRow, {
725
+ refusals: data.refusals
726
+ })
727
+ ]
728
+ }),
729
+ /*#__PURE__*/ _jsxs(Stack, {
730
+ spacing: 0.5,
731
+ children: [
732
+ /*#__PURE__*/ _jsx(Typography, {
733
+ variant: "overline",
734
+ color: "text.secondary",
735
+ children: 'Generation jobs'
736
+ }),
737
+ /*#__PURE__*/ _jsx(JobsSection, {
738
+ jobs: data.jobs,
739
+ onGiveBack: giveBackJob
740
+ })
741
+ ]
742
+ }),
743
+ data.tokens ? /*#__PURE__*/ _jsxs(Stack, {
744
+ spacing: 0.5,
745
+ children: [
746
+ /*#__PURE__*/ _jsx(Typography, {
747
+ variant: "overline",
748
+ color: "text.secondary",
749
+ children: `Tokens · ${data.month}`
750
+ }),
751
+ /*#__PURE__*/ _jsx(TokensSection, {
752
+ tokens: data.tokens
753
+ })
754
+ ]
755
+ }) : null,
756
+ /*#__PURE__*/ _jsxs(Stack, {
757
+ spacing: 0.5,
758
+ children: [
759
+ /*#__PURE__*/ _jsx(Typography, {
760
+ variant: "overline",
761
+ color: "text.secondary",
762
+ children: 'Top users this month'
763
+ }),
764
+ data.users.length === 0 ? /*#__PURE__*/ _jsx(Typography, {
765
+ variant: "body2",
766
+ color: "text.secondary",
767
+ children: 'No AI usage attributed to a person this month.'
768
+ }) : /*#__PURE__*/ _jsx(TopUsersSection, {
769
+ users: data.users
770
+ })
771
+ ]
772
+ }),
773
+ /*#__PURE__*/ _jsxs(Stack, {
774
+ spacing: 0.5,
775
+ children: [
776
+ /*#__PURE__*/ _jsx(Typography, {
777
+ variant: "overline",
778
+ color: "text.secondary",
779
+ children: 'Margin'
780
+ }),
781
+ /*#__PURE__*/ _jsxs(Alert, {
782
+ severity: marginTone(data.margin),
783
+ children: [
784
+ `Spend ${usd(data.margin.spendUsd)} against ${usd(data.margin.revenueUsd)} in — ` + `${usd(data.margin.addonRevenueUsd)} add-on, ${usd(data.margin.planAssistShareUsd)} plan assist share, ` + `${usd(data.margin.overageRevenueUsd)} overage.`,
785
+ data.margin.underwater ? ' Spend exceeds the add-on plus the plan’s assist share.' : '',
786
+ data.margin.multiple >= 1 ? ` ${data.margin.multiple}× the ${usd(data.margin.thresholdUsd)} staff review threshold.` : ''
787
+ ]
788
+ }),
789
+ data.margin.contribution.marginPct !== null ? /*#__PURE__*/ _jsx(Typography, {
790
+ variant: "caption",
791
+ color: "text.secondary",
792
+ children: `Whole-org contribution margin ${Math.round(data.margin.contribution.marginPct * 100)}% on the ${data.margin.contribution.month} rollup with this month’s AI spend.`
793
+ }) : null
794
+ ]
795
+ }),
796
+ /*#__PURE__*/ _jsxs(Stack, {
797
+ spacing: 0.5,
798
+ children: [
799
+ /*#__PURE__*/ _jsx(Typography, {
800
+ variant: "overline",
801
+ color: "text.secondary",
802
+ children: 'Actions'
803
+ }),
804
+ /*#__PURE__*/ _jsxs(Typography, {
805
+ variant: "body2",
806
+ children: [
807
+ 'Credit override and feature toggles: ',
808
+ /*#__PURE__*/ _jsx(AppLink, {
809
+ href: buildRoute(Route.ADMIN_ORGS),
810
+ underline: "hover",
811
+ children: 'the entitlement override editor'
812
+ }),
813
+ ' (also under Staff actions above). Pause AI for this org: ',
814
+ /*#__PURE__*/ _jsx(AppLink, {
815
+ href: buildRoute(Route.ADMIN_LOCKDOWN),
816
+ underline: "hover",
817
+ children: 'Lockdown'
818
+ }),
819
+ ', feature keys ai-assist and ai-generate scoped to the org.'
820
+ ]
821
+ })
822
+ ]
823
+ })
824
+ ]
825
+ })
826
+ ]
742
827
  });
743
828
  };
744
829
  StaffOrgAiCard.displayName = 'StaffOrgAiCard';