@aglyn/plugins-ai 1.0.0-beta.254 → 1.0.0-beta.256

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 (71) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/assist-signals-page.component.js +5 -6
  3. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  4. package/src/lib/components/staff-org-ai-card.component.d.ts +0 -10
  5. package/src/lib/components/staff-org-ai-card.component.js +10 -79
  6. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  7. package/src/lib/components/staff-org-ai-conversations.component.js +8 -41
  8. package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -1
  9. package/src/lib/components/staff-user-ai-usage-card.component.js +2 -1
  10. package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -1
  11. package/src/lib/jobs/ai-job-page-language.d.ts +14 -1
  12. package/src/lib/jobs/ai-job-page-language.js +18 -3
  13. package/src/lib/jobs/ai-job-page-language.js.map +1 -1
  14. package/src/lib/jobs/ai-job-page-step.js +17 -3
  15. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  16. package/src/lib/jobs/ai-job-plan-step.js +4 -0
  17. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  18. package/src/lib/jobs/ai-layout-stock-photos.d.ts +30 -0
  19. package/src/lib/jobs/ai-layout-stock-photos.js +76 -8
  20. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  21. package/src/lib/jobs/fixtures/ai-dental-site-plan-recording.d.ts +95 -0
  22. package/src/lib/jobs/fixtures/ai-dental-site-plan-recording.js +137 -0
  23. package/src/lib/jobs/fixtures/ai-dental-site-plan-recording.js.map +1 -0
  24. package/src/lib/layout-language/ai-layout-labels.d.ts +2 -0
  25. package/src/lib/layout-language/ai-layout-labels.js +12 -6
  26. package/src/lib/layout-language/ai-layout-labels.js.map +1 -1
  27. package/src/lib/layout-language/ai-layout-pictures.d.ts +12 -3
  28. package/src/lib/layout-language/ai-layout-pictures.js +6 -3
  29. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  30. package/src/lib/model/ai-products-bulk.d.ts +48 -0
  31. package/src/lib/model/ai-products-bulk.js +56 -0
  32. package/src/lib/model/ai-products-bulk.js.map +1 -0
  33. package/src/lib/model/ai-products.d.ts +2 -4
  34. package/src/lib/model/ai-products.js +4 -9
  35. package/src/lib/model/ai-products.js.map +1 -1
  36. package/src/lib/providers/anthropic.js +26 -5
  37. package/src/lib/providers/anthropic.js.map +1 -1
  38. package/src/lib/providers/contract.d.ts +10 -0
  39. package/src/lib/providers/contract.js.map +1 -1
  40. package/src/lib/runtime/ai-doctrine-validators.d.ts +20 -0
  41. package/src/lib/runtime/ai-doctrine-validators.js +50 -0
  42. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  43. package/src/lib/runtime/ai-doctrine.d.ts +11 -0
  44. package/src/lib/runtime/ai-doctrine.js +9 -4
  45. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  46. package/src/lib/runtime/ai-runtime.d.ts +6 -0
  47. package/src/lib/runtime/ai-runtime.js +10 -0
  48. package/src/lib/runtime/ai-runtime.js.map +1 -1
  49. package/src/lib/server/ai-admin-conversations.d.ts +2 -0
  50. package/src/lib/server/ai-admin-conversations.js +40 -7
  51. package/src/lib/server/ai-admin-conversations.js.map +1 -1
  52. package/src/lib/server/ai-admin-org.js +28 -2
  53. package/src/lib/server/ai-admin-org.js.map +1 -1
  54. package/src/lib/server/ai-admin-orgs-spend.js +19 -1
  55. package/src/lib/server/ai-admin-orgs-spend.js.map +1 -1
  56. package/src/lib/server/ai-admin-signals.js +11 -3
  57. package/src/lib/server/ai-admin-signals.js.map +1 -1
  58. package/src/lib/server/ai-admin-user.js +11 -1
  59. package/src/lib/server/ai-admin-user.js.map +1 -1
  60. package/src/lib/usage/ai-usage-wire.d.ts +5 -2
  61. package/src/lib/usage/ai-usage-wire.js +5 -2
  62. package/src/lib/usage/ai-usage-wire.js.map +1 -1
  63. package/src/lib/usage/assist-signals-view.d.ts +123 -0
  64. package/src/lib/usage/assist-signals-view.js +151 -0
  65. package/src/lib/usage/assist-signals-view.js.map +1 -0
  66. package/src/lib/usage/staff-org-ai-conversations-view.d.ts +51 -0
  67. package/src/lib/usage/staff-org-ai-conversations-view.js +127 -0
  68. package/src/lib/usage/staff-org-ai-conversations-view.js.map +1 -0
  69. package/src/lib/usage/staff-org-ai-view.d.ts +181 -0
  70. package/src/lib/usage/staff-org-ai-view.js +263 -0
  71. package/src/lib/usage/staff-org-ai-view.js.map +1 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/staff-org-ai-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { aiAddonName } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiOpenInNew } from '@aglyn/shared-data-mdi'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Chip,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type {\n StaffOrgAiJob,\n StaffOrgAiJobs,\n StaffOrgAiMargin,\n StaffOrgAiOverage,\n StaffOrgAiPool,\n StaffOrgAiRefusals,\n StaffOrgAiResponse,\n StaffOrgAiTokens,\n StaffOrgAiUser,\n} from '../usage/staff-org-ai'\nimport {\n StaffCreditReturnActions,\n StaffCreditReturnDialog,\n} from './staff-ai-credit-return.component'\n\n/**\n * THE STAFF AI CARD (AGL-2930): everything about one org's AI usage on the\n * page staff already open to look at that org.\n *\n * Read through `/api/ai/admin/org`, one request on mount. Every figure on it\n * is the route's, and every figure the route serves is composed from the\n * same helpers the meter refuses with — see `usage/staff-org-ai.ts`. This\n * file only lays them out.\n *\n * ## What each section is for\n *\n * - **Add-on** — is the org paying for AI, at what, since when.\n * - **Credit pool** — the band as three NAMED parts, because \"why does\n * this org have 13,000 credits\" has three answers (the plan, an override,\n * the add-on) and a single total cannot say which.\n * - **Overage** — what is past the band, what it is sold at, and the two\n * controls the org itself holds over it.\n * - **Refusals** — how often, and why, the gate said no this month.\n * - **Jobs** and **Top users** — the generative and per-person halves,\n * each of which says plainly when its rollup has nothing yet.\n * - **Tokens** — what each kind of request costs per request, what it\n * sends and generates, and how much of its prompt the cache served.\n * - **Margin** — spend against what AI brings in, red when it is over.\n * - **Actions** — where the override editor and the AI pause live.\n *\n * The header carries the card's two acts (AGL-3595): give credits back to\n * the band (and, on a Free workspace, to the owner's allowance), or reset\n * the month — and each recent job offers the same give-back, prefilled with\n * that job's spend.\n */\n\nconst credits = (value: number | null | undefined): string =>\n value == null ? '—' : Math.round(value).toLocaleString()\n\n/**\n * Four decimals under a dollar, two above (the assist-signals page's rule):\n * a month that really cost eight cents must not read as `$0.00`.\n */\nconst usd = (value: number | null | undefined): string => {\n if (value == null) return '—'\n const amount = Number(value) || 0\n return `$${amount.toFixed(Math.abs(amount) < 1 ? 4 : 2)}`\n}\n\nconst when = (iso: string | null): string =>\n iso ? new Date(iso).toLocaleDateString() : '—'\n\n/** The pool as one sentence naming every part that is non-zero. */\nexport function poolPartsSentence(pool: StaffOrgAiPool): string {\n const parts: string[] = []\n if (pool.overrideCredits !== null) {\n parts.push(`${credits(pool.overrideCredits)} staff override`)\n } else {\n parts.push(`${credits(pool.planCredits)} plan band`)\n }\n if (pool.addonCredits > 0) {\n parts.push(`${credits(pool.addonCredits)} ${aiAddonName()} add-on`)\n }\n return parts.join(' + ')\n}\n\n/**\n * The overage's state as one line: sold, walled, or capped — or, for an\n * uncapped staff comp (AGL-3049), nothing to sell past at all.\n */\nexport function overageStateSentence(\n overage: StaffOrgAiOverage,\n uncapped = false,\n): string {\n if (uncapped) {\n return 'Nothing is sold past a band: an uncapped staff comp has none, and bills nothing.'\n }\n if (overage.hardCap) return 'Stopped at the band — the org’s band switch is on.'\n if (!overage.sellsOverage) return 'Not sold past the band on this plan.'\n const rate =\n overage.rateUsdPer1k === null ? '' : ` at ${usd(overage.rateUsdPer1k)} per 1,000`\n if (overage.capUsd !== null) {\n return overage.capReached\n ? `Stopped — the org’s ${usd(overage.capUsd)} overage ceiling is reached.`\n : `Sold${rate}, up to the org’s ${usd(overage.capUsd)} ceiling.`\n }\n return `Sold${rate}, no ceiling set.`\n}\n\n/** Whether the margin section renders red. */\nexport function marginTone(margin: StaffOrgAiMargin): 'error' | 'warning' | 'success' {\n if (margin.underwater) return 'error'\n if (margin.multiple >= 1) return 'warning'\n return 'success'\n}\n\nfunction RefusalsRow({ refusals }: { refusals: StaffOrgAiRefusals }) {\n const labels: Record<keyof Omit<StaffOrgAiRefusals, 'total'>, string> = {\n band: 'band',\n cap: 'ceiling',\n messages: 'messages',\n budget: 'operator backstop',\n // A hard allotment a manager set on a member, a collaborator or a site\n // (AGL-2942).\n allotment: 'allotments',\n // The Free taste's rungs (AGL-2925): only a Free workspace collects these.\n account: 'free account',\n requests: 'free requests/day',\n refusals: 'free refusals/day',\n platform: 'platform ceiling',\n }\n return (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {(Object.keys(labels) as Array<keyof typeof labels>).map((reason) => {\n // A reason the answering server does not yet count reads as zero,\n // so a console newer than its route draws the row rather than\n // failing the card.\n const count = Number(refusals[reason] ?? 0)\n return (\n <Chip\n key={reason}\n size=\"small\"\n variant={count > 0 ? 'filled' : 'outlined'}\n color={count > 0 ? 'warning' : 'default'}\n label={`${labels[reason]} ${count.toLocaleString()}`}\n />\n )\n })}\n </Stack>\n )\n}\n\nfunction JobsSection({\n jobs,\n onGiveBack,\n}: {\n jobs: StaffOrgAiJobs | null\n onGiveBack: (job: StaffOrgAiJob) => void\n}) {\n const columns = useMemo(() => jobColumns(onGiveBack), [onGiveBack])\n if (jobs === null) {\n return (\n <Alert severity=\"warning\">\n {'Could not read the generation jobs — a failed read, not an idle org.'}\n </Alert>\n )\n }\n const total = Object.values(jobs.counts).reduce((sum, n) => sum + n, 0)\n if (total === 0) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No generation jobs yet.'}\n </Typography>\n )\n }\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {(['queued', 'running', 'needs_input', 'needs_review', 'failed'] as const).map((status) => (\n <Chip\n key={status}\n size=\"small\"\n color={\n status === 'failed' && jobs.counts[status] > 0\n ? 'error'\n : (status === 'needs_input' || status === 'needs_review') &&\n jobs.counts[status] > 0\n ? 'warning'\n : 'default'\n }\n label={`${status.replace('_', ' ')} ${jobs.counts[status]}`}\n />\n ))}\n <Chip size=\"small\" variant=\"outlined\" label={`done ${jobs.counts.done}`} />\n </Stack>\n {jobs.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Counted from the newest jobs only — these counts are a floor.'}\n </Typography>\n ) : null}\n {/* A record list: the grid scrolls its own columns, so a job id and a\n starter's uid never push the card's content past its edge. */}\n <ListTable\n rows={jobs.recent}\n columns={columns}\n getRowId={(job: StaffOrgAiJob) => job.id}\n rowHeight={TABLE_ROW_HEIGHT}\n noRowsLabel=\"No recent jobs\"\n // The route's ten newest jobs: a bounded readout, so no filter over\n // the rows it holds (AGL-3321).\n disableColumnFilter\n quickFilter={false}\n />\n </Stack>\n )\n}\n\n/**\n * The recent jobs, one column per fact. Who started a job is a column of\n * its own rather than a link tucked beside the date, so it sorts like the\n * rest; the credits sort by what was SPENT, which is the figure the\n * pool was charged. The row's action gives that spend back (AGL-3595).\n */\nconst jobColumns = (\n onGiveBack: (job: StaffOrgAiJob) => void,\n): GridColDef<StaffOrgAiJob>[] => [\n {\n field: 'id',\n headerName: 'Job',\n flex: 1,\n minWidth: 200,\n renderCell: ({ row }) => (\n <Typography variant=\"body2\" noWrap title={row.id} sx={{ fontFamily: 'monospace' }}>\n {row.id}\n </Typography>\n ),\n },\n {\n field: 'kind',\n headerName: 'Kind',\n width: 120,\n valueFormatter: (value: string) => value || '—',\n },\n {\n field: 'status',\n headerName: 'Status',\n width: 130,\n valueFormatter: (value: string) => String(value ?? '').replace('_', ' '),\n },\n {\n field: 'creditsSpent',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 140,\n valueFormatter: (value: number, row) =>\n `${credits(value)} / ${credits(row.creditsReserved)}`,\n },\n {\n field: 'createdAt',\n headerName: 'Started',\n width: 120,\n valueFormatter: (value: string | null) => when(value),\n },\n {\n field: 'createdBy',\n headerName: 'Started by',\n flex: 1,\n minWidth: 240,\n renderCell: ({ row }) =>\n row.createdBy ? (\n <AppLink\n href={buildRoute(Route.ADMIN_USER_DETAIL, { uid: row.createdBy })}\n variant=\"body2\"\n underline=\"hover\"\n noWrap\n sx={{ fontFamily: 'monospace' }}\n >\n {row.createdBy}\n </AppLink>\n ) : (\n '—'\n ),\n },\n listActionsColumn((row: StaffOrgAiJob) => (\n <ListRowActions\n label={row.id}\n items={[\n {\n key: 'give-back',\n label: 'Give back this job’s credits',\n disabled: row.creditsSpent <= 0,\n disabledReason: row.creditsSpent <= 0 ? 'This job spent no credits.' : undefined,\n onClick: () => onGiveBack(row),\n },\n ]}\n />\n )),\n]\n\nconst tokenCount = (value: number): string => Math.round(value).toLocaleString()\n\nconst rate = (value: number | null): string =>\n value == null ? '—' : `${Math.round(value * 100)}%`\n\n/** The month's tokens, and what each kind of request spends of them (AGL-2937). */\nfunction TokensSection({ tokens }: { tokens: StaffOrgAiTokens }) {\n const { total } = tokens\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Typography variant=\"body2\">\n {`${tokenCount(total.input)} sent · ${tokenCount(total.cached)} read from cache · ${tokenCount(total.cacheWrite)} written to cache · ${tokenCount(total.output)} generated`}\n </Typography>\n <Chip\n size=\"small\"\n color={tokens.cacheHitRate != null && tokens.cacheHitRate >= 0.5 ? 'success' : 'default'}\n label={`cache hit rate ${rate(tokens.cacheHitRate)}`}\n />\n </Stack>\n {tokens.kinds.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No model requests recorded by kind this month.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Kind'}</TableCell>\n <TableCell align=\"right\">{'Requests'}</TableCell>\n <TableCell align=\"right\">{'Per request'}</TableCell>\n <TableCell align=\"right\">{'Sent'}</TableCell>\n <TableCell align=\"right\">{'From cache'}</TableCell>\n <TableCell align=\"right\">{'Generated'}</TableCell>\n <TableCell align=\"right\">{'Cache hit'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {tokens.kinds.map((row) => (\n <TableRow key={row.kind}>\n <TableCell>{row.kind}</TableCell>\n <TableCell align=\"right\">{row.requests.toLocaleString()}</TableCell>\n <TableCell align=\"right\">{usd(row.costPerRequestUsd)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.input)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.cached)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.output)}</TableCell>\n <TableCell align=\"right\">{rate(row.cacheHitRate)}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n </Stack>\n )\n}\n\n/**\n * The month's top spenders, one row per person. The row opens the account;\n * the uid rides beneath the roster's name because two people can share one.\n */\nfunction TopUsersSection({ users }: { users: StaffOrgAiUser[] }) {\n const router = useRouter()\n const columns = useMemo<GridColDef<StaffOrgAiUser>[]>(\n () => [\n {\n field: 'name',\n headerName: 'Person',\n flex: 1,\n minWidth: 220,\n renderCell: ({ row }) => (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap title={row.name}>\n {row.name}\n </Typography>\n {row.name !== row.uid ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n noWrap\n sx={{ fontFamily: 'monospace' }}\n >\n {row.uid}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n {\n field: 'credits',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => credits(value),\n },\n {\n field: 'share',\n headerName: 'Share',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 90,\n valueFormatter: (value: number) => `${Math.round(value * 100)}%`,\n },\n {\n field: 'estCostUsd',\n headerName: 'Provider $',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 120,\n valueFormatter: (value: number) => usd(value),\n },\n {\n field: 'requests',\n headerName: 'Requests',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n {\n field: 'refusals',\n headerName: 'Refusals',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n listActionsColumn((row: StaffOrgAiUser) => (\n <ListRowActions\n label={row.name}\n quick={{\n icon: mdiOpenInNew.path,\n label: 'View account',\n newTab: true,\n to: buildRoute(Route.ADMIN_USER_DETAIL, { uid: row.uid }),\n }}\n items={[]}\n />\n )),\n ],\n [],\n )\n return (\n <ListTable\n rows={users}\n columns={columns}\n getRowId={(row: StaffOrgAiUser) => row.uid}\n rowHeight={TABLE_ROW_HEIGHT}\n onOpen={(uid) => router.push(buildRoute(Route.ADMIN_USER_DETAIL, { uid }))}\n // The route's ten dearest members: a bounded readout, so no filter\n // over the rows it holds (AGL-3321).\n disableColumnFilter\n quickFilter={false}\n />\n )\n}\n\nconst StaffOrgAiCard = ({ orgId }: { orgId: string }) => {\n const { data: user } = useUser()\n // Keyed on who is signed in, not on the user object's identity.\n const signedInUid = user?.uid ?? null\n const userRef = useRef(user)\n userRef.current = user\n const [data, setData] = useState<StaffOrgAiResponse | null>(null)\n const [ready, setReady] = useState(false)\n const [error, setError] = useState<string | null>(null)\n // Bumped after a give-back, so the card re-reads the figures it changed.\n const [version, setVersion] = useState(0)\n const [notice, setNotice] = useState<string | null>(null)\n const [returning, setReturning] = useState<{\n mode: 'give' | 'reset'\n jobId?: string\n jobCredits?: number\n } | null>(null)\n const giveBackJob = useCallback(\n (job: StaffOrgAiJob) =>\n setReturning({ mode: 'give', jobId: job.id, jobCredits: job.creditsSpent }),\n [],\n )\n\n useEffect(() => {\n // Mounted only for a confirmed staff reader — the `staffOrg` zone sits\n // inside the page's `StaffOnly`, which renders nothing while the claim\n // loads — because the route records an access row on every successful\n // open, and an open the page did not mean to make would be an access\n // nobody made.\n const user = userRef.current\n if (!orgId || !signedInUid || !user) return undefined\n let active = true\n setReady(false)\n setError(null)\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/admin/org?orgId=${encodeURIComponent(orgId)}`,\n )\n const payload = await response.json().catch(() => null)\n if (!active) return\n if (!response.ok) {\n setError(payload?.error ?? 'AI lookup failed')\n setData(null)\n } else {\n setData(payload as StaffOrgAiResponse)\n }\n } catch {\n if (active) {\n setError('AI lookup failed')\n setData(null)\n }\n } finally {\n if (active) setReady(true)\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, signedInUid, version])\n\n const name = aiAddonName()\n return (\n <CardDisplay\n header={name}\n help={pluginDocsHelp('aiMonitoring', {\n anchor: '#the-ai-card',\n excerpt:\n '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.',\n })}\n HeaderProps={{\n action: (\n <StaffCreditReturnActions\n disabled={!data}\n onGive={() => setReturning({ mode: 'give' })}\n onReset={() => setReturning({ mode: 'reset' })}\n />\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {data ? (\n <StaffCreditReturnDialog\n open={returning !== null}\n mode={returning?.mode ?? 'give'}\n target={{ orgId }}\n workspace={{ used: data.pool.usedCredits, limit: data.pool.totalCredits }}\n account={\n data.account\n ? { used: data.account.usedCredits, limit: data.account.limitCredits }\n : null\n }\n jobId={returning?.jobId ?? null}\n jobCredits={returning?.jobCredits ?? null}\n onClose={() => setReturning(null)}\n onReturned={(message) => {\n setReturning(null)\n setNotice(message)\n setVersion((current) => current + 1)\n }}\n />\n ) : null}\n {notice ? (\n <Alert severity=\"success\" onClose={() => setNotice(null)} sx={{ mb: 2 }}>\n {notice}\n </Alert>\n ) : null}\n {!ready ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : error || !data ? (\n <Alert severity=\"warning\">\n {`Could not read this organization’s AI usage — ${error ?? 'a failed read'}, not zero usage.`}\n </Alert>\n ) : (\n <Stack spacing={2}>\n {/* Add-on */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Add-on'}\n </Typography>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Chip\n size=\"small\"\n color={data.addon.on ? 'primary' : 'default'}\n label={data.addon.on ? `${name} on` : `${name} off`}\n />\n {data.addon.priceUsd !== null ? (\n <Typography variant=\"body2\">\n {`${usd(data.addon.priceUsd)}/mo on this plan`}\n </Typography>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Not sold on this plan'}\n </Typography>\n )}\n {data.addon.on ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {data.addon.since\n ? `since ${when(data.addon.since)}`\n : data.addon.sinceSource === 'unavailable'\n ? 'since — (Stripe not reachable)'\n : 'since — (no subscription item)'}\n </Typography>\n ) : null}\n </Stack>\n </Stack>\n\n {/* Credit pool */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Credit pool · ${data.month}`}\n </Typography>\n {data.pool.totalCredits === null && data.pool.uncapped ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Uncapped staff comp — no AI credit band. Bounded only by the message cap and an operator’s explicit spend ceiling.'}\n </Typography>\n ) : data.pool.totalCredits === null ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI credit band on this plan — bounded by the message cap and the operator backstop.'}\n </Typography>\n ) : (\n <Typography variant=\"body2\">\n {`${credits(data.pool.totalCredits)} credits = ${poolPartsSentence(data.pool)}`}\n </Typography>\n )}\n <Typography variant=\"body2\">\n {`Used ${credits(data.pool.usedCredits)} credits (${usd(data.pool.providerUsd)} provider spend)`}\n {data.pool.remainingCredits !== null\n ? ` · ${credits(data.pool.remainingCredits)} remaining`\n : ''}\n </Typography>\n {data.pool.returnedCredits ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${credits(data.pool.returnedCredits)} credits given back this month — already taken off the figure above.`}\n </Typography>\n ) : null}\n {data.account ? (\n <Typography variant=\"body2\">\n {`Owner’s Free allowance: ${credits(data.account.usedCredits)} of ${credits(data.account.limitCredits)} used across their free workspaces` +\n (data.account.returnedCredits\n ? ` (${credits(data.account.returnedCredits)} given back)`\n : '')}\n </Typography>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`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`}\n </Typography>\n </Stack>\n\n {/* Overage */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Overage'}\n </Typography>\n <Typography variant=\"body2\">\n {`${credits(data.overage.overageCredits)} credits over the band · ${usd(data.overage.accruedUsd)} accrued`}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {overageStateSentence(data.overage, data.pool.uncapped === true)}\n </Typography>\n </Stack>\n\n {/* Refusals */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Refusals this month · ${data.refusals.total.toLocaleString()}`}\n </Typography>\n <RefusalsRow refusals={data.refusals} />\n </Stack>\n\n {/* Jobs */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Generation jobs'}\n </Typography>\n <JobsSection jobs={data.jobs} onGiveBack={giveBackJob} />\n </Stack>\n\n {/* Tokens — absent from a route older than the card */}\n {data.tokens ? (\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Tokens · ${data.month}`}\n </Typography>\n <TokensSection tokens={data.tokens} />\n </Stack>\n ) : null}\n\n {/* Top users */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Top users this month'}\n </Typography>\n {data.users.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI usage attributed to a person this month.'}\n </Typography>\n ) : (\n <TopUsersSection users={data.users} />\n )}\n </Stack>\n\n {/* Margin */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Margin'}\n </Typography>\n <Alert severity={marginTone(data.margin)}>\n {`Spend ${usd(data.margin.spendUsd)} against ${usd(data.margin.revenueUsd)} in — ` +\n `${usd(data.margin.addonRevenueUsd)} add-on, ${usd(data.margin.planAssistShareUsd)} plan assist share, ` +\n `${usd(data.margin.overageRevenueUsd)} overage.`}\n {data.margin.underwater\n ? ' Spend exceeds the add-on plus the plan’s assist share.'\n : ''}\n {data.margin.multiple >= 1\n ? ` ${data.margin.multiple}× the ${usd(data.margin.thresholdUsd)} staff review threshold.`\n : ''}\n </Alert>\n {data.margin.contribution.marginPct !== null ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Whole-org contribution margin ${Math.round(data.margin.contribution.marginPct * 100)}% on the ${data.margin.contribution.month} rollup with this month’s AI spend.`}\n </Typography>\n ) : null}\n </Stack>\n\n {/* Actions */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Actions'}\n </Typography>\n <Typography variant=\"body2\">\n {'Credit override and feature toggles: '}\n <AppLink href={buildRoute(Route.ADMIN_ORGS)} underline=\"hover\">\n {'the entitlement override editor'}\n </AppLink>\n {' (also under Staff actions above). Pause AI for this org: '}\n <AppLink href={buildRoute(Route.ADMIN_LOCKDOWN)} underline=\"hover\">\n {'Lockdown'}\n </AppLink>\n {', feature keys ai-assist and ai-generate scoped to the org.'}\n </Typography>\n </Stack>\n </Stack>\n )}\n </CardDisplay>\n )\n}\nStaffOrgAiCard.displayName = 'StaffOrgAiCard'\n\nexport default StaffOrgAiCard\n"],"names":["aiAddonName","buildRoute","Route","pluginDocsHelp","mdiOpenInNew","AppLink","CardDisplay","ListRowActions","ListTable","listActionsColumn","ScrollTable","TABLE_ROW_HEIGHT","useUser","authorizedFetch","Alert","Chip","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useRouter","useCallback","useEffect","useMemo","useRef","useState","StaffCreditReturnActions","StaffCreditReturnDialog","credits","value","Math","round","toLocaleString","usd","amount","Number","toFixed","abs","when","iso","Date","toLocaleDateString","poolPartsSentence","pool","parts","overrideCredits","push","planCredits","addonCredits","join","overageStateSentence","overage","uncapped","hardCap","sellsOverage","rate","rateUsdPer1k","capUsd","capReached","marginTone","margin","underwater","multiple","RefusalsRow","refusals","labels","band","cap","messages","budget","allotment","account","requests","platform","direction","spacing","useFlexGap","sx","flexWrap","Object","keys","map","reason","count","size","variant","color","label","JobsSection","jobs","onGiveBack","columns","jobColumns","severity","total","values","counts","reduce","sum","n","status","replace","done","truncated","rows","recent","getRowId","job","id","rowHeight","noRowsLabel","disableColumnFilter","quickFilter","field","headerName","flex","minWidth","renderCell","row","noWrap","title","fontFamily","width","valueFormatter","String","type","align","headerAlign","creditsReserved","createdBy","href","ADMIN_USER_DETAIL","uid","underline","items","key","disabled","creditsSpent","disabledReason","undefined","onClick","tokenCount","TokensSection","tokens","alignItems","input","cached","cacheWrite","output","cacheHitRate","kinds","length","kind","costPerRequestUsd","TopUsersSection","users","router","name","quick","icon","path","newTab","to","onOpen","StaffOrgAiCard","orgId","data","user","signedInUid","userRef","current","setData","ready","setReady","error","setError","version","setVersion","notice","setNotice","returning","setReturning","giveBackJob","mode","jobId","jobCredits","active","response","encodeURIComponent","payload","json","catch","ok","header","help","anchor","excerpt","HeaderProps","action","onGive","onReset","contentGutterX","contentGutterY","open","target","workspace","used","usedCredits","limit","totalCredits","limitCredits","onClose","onReturned","message","mb","addon","on","priceUsd","since","sinceSource","month","providerUsd","remainingCredits","returnedCredits","projectedCredits","projectedUsd","deflected","overageCredits","accruedUsd","spendUsd","revenueUsd","addonRevenueUsd","planAssistShareUsd","overageRevenueUsd","thresholdUsd","contribution","marginPct","ADMIN_ORGS","ADMIN_LOCKDOWN","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/staff-org-ai-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { aiAddonName } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiOpenInNew } from '@aglyn/shared-data-mdi'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Chip,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type {\n StaffOrgAiJob,\n StaffOrgAiJobs,\n StaffOrgAiRefusals,\n StaffOrgAiResponse,\n StaffOrgAiTokens,\n StaffOrgAiUser,\n} from '../usage/staff-org-ai'\nimport {\n allowanceSentence,\n credits,\n marginTone,\n overageStateSentence,\n poolPartsSentence,\n rate,\n refusalChips,\n tokenCount,\n usd,\n JOB_CHIP_STATUSES,\n} from '../usage/staff-org-ai-view'\nimport {\n StaffCreditReturnActions,\n StaffCreditReturnDialog,\n} from './staff-ai-credit-return.component'\n\n/**\n * THE STAFF AI CARD (AGL-2930): everything about one org's AI usage on the\n * page staff already open to look at that org.\n *\n * Read through `/api/ai/admin/org`, one request on mount. Every figure on it\n * is the route's, and every figure the route serves is composed from the\n * same helpers the meter refuses with — see `usage/staff-org-ai.ts`. This\n * file only lays them out.\n *\n * ## What each section is for\n *\n * - **Add-on** — is the org paying for AI, at what, since when.\n * - **Credit pool** — the band as three NAMED parts, because \"why does\n * this org have 13,000 credits\" has three answers (the plan, an override,\n * the add-on) and a single total cannot say which.\n * - **Overage** — what is past the band, what it is sold at, and the two\n * controls the org itself holds over it.\n * - **Refusals** — how often, and why, the gate said no this month.\n * - **Jobs** and **Top users** — the generative and per-person halves,\n * each of which says plainly when its rollup has nothing yet.\n * - **Tokens** — what each kind of request costs per request, what it\n * sends and generates, and how much of its prompt the cache served.\n * - **Margin** — spend against what AI brings in, red when it is over.\n * - **Actions** — where the override editor and the AI pause live.\n *\n * The header carries the card's two acts (AGL-3595): give credits back to\n * the band (and, on a Free workspace, to the owner's allowance), or reset\n * the month — and each recent job offers the same give-back, prefilled with\n * that job's spend.\n */\n\nconst when = (iso: string | null): string =>\n iso ? new Date(iso).toLocaleDateString() : '—'\n\nfunction RefusalsRow({ refusals }: { refusals: StaffOrgAiRefusals }) {\n return (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {refusalChips(refusals).map((chip) => (\n <Chip\n key={chip.label}\n size=\"small\"\n variant={chip.count > 0 ? 'filled' : 'outlined'}\n color={chip.count > 0 ? 'warning' : 'default'}\n label={`${chip.label} ${chip.count.toLocaleString()}`}\n />\n ))}\n </Stack>\n )\n}\n\nfunction JobsSection({\n jobs,\n onGiveBack,\n}: {\n jobs: StaffOrgAiJobs | null\n onGiveBack: (job: StaffOrgAiJob) => void\n}) {\n const columns = useMemo(() => jobColumns(onGiveBack), [onGiveBack])\n if (jobs === null) {\n return (\n <Alert severity=\"warning\">\n {'Could not read the generation jobs — a failed read, not an idle org.'}\n </Alert>\n )\n }\n const total = Object.values(jobs.counts).reduce((sum, n) => sum + n, 0)\n if (total === 0) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No generation jobs yet.'}\n </Typography>\n )\n }\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {JOB_CHIP_STATUSES.map((status) => (\n <Chip\n key={status}\n size=\"small\"\n color={\n status === 'failed' && jobs.counts[status] > 0\n ? 'error'\n : (status === 'needs_input' || status === 'needs_review') &&\n jobs.counts[status] > 0\n ? 'warning'\n : 'default'\n }\n label={`${status.replace('_', ' ')} ${jobs.counts[status]}`}\n />\n ))}\n <Chip size=\"small\" variant=\"outlined\" label={`done ${jobs.counts.done}`} />\n </Stack>\n {jobs.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Counted from the newest jobs only — these counts are a floor.'}\n </Typography>\n ) : null}\n {/* A record list: the grid scrolls its own columns, so a job id and a\n starter's uid never push the card's content past its edge. */}\n <ListTable\n rows={jobs.recent}\n columns={columns}\n getRowId={(job: StaffOrgAiJob) => job.id}\n rowHeight={TABLE_ROW_HEIGHT}\n noRowsLabel=\"No recent jobs\"\n // The route's ten newest jobs: a bounded readout, so no filter over\n // the rows it holds (AGL-3321).\n disableColumnFilter\n quickFilter={false}\n />\n </Stack>\n )\n}\n\n/**\n * The recent jobs, one column per fact. Who started a job is a column of\n * its own rather than a link tucked beside the date, so it sorts like the\n * rest; the credits sort by what was SPENT, which is the figure the\n * pool was charged. The row's action gives that spend back (AGL-3595).\n */\nconst jobColumns = (\n onGiveBack: (job: StaffOrgAiJob) => void,\n): GridColDef<StaffOrgAiJob>[] => [\n {\n field: 'id',\n headerName: 'Job',\n flex: 1,\n minWidth: 200,\n renderCell: ({ row }) => (\n <Typography variant=\"body2\" noWrap title={row.id} sx={{ fontFamily: 'monospace' }}>\n {row.id}\n </Typography>\n ),\n },\n {\n field: 'kind',\n headerName: 'Kind',\n width: 120,\n valueFormatter: (value: string) => value || '—',\n },\n {\n field: 'status',\n headerName: 'Status',\n width: 130,\n valueFormatter: (value: string) => String(value ?? '').replace('_', ' '),\n },\n {\n field: 'creditsSpent',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 140,\n valueFormatter: (value: number, row) =>\n `${credits(value)} / ${credits(row.creditsReserved)}`,\n },\n {\n field: 'createdAt',\n headerName: 'Started',\n width: 120,\n valueFormatter: (value: string | null) => when(value),\n },\n {\n field: 'createdBy',\n headerName: 'Started by',\n flex: 1,\n minWidth: 240,\n renderCell: ({ row }) =>\n row.createdBy ? (\n <AppLink\n href={buildRoute(Route.ADMIN_USER_DETAIL, { uid: row.createdBy })}\n variant=\"body2\"\n underline=\"hover\"\n noWrap\n sx={{ fontFamily: 'monospace' }}\n >\n {row.createdBy}\n </AppLink>\n ) : (\n '—'\n ),\n },\n listActionsColumn((row: StaffOrgAiJob) => (\n <ListRowActions\n label={row.id}\n items={[\n {\n key: 'give-back',\n label: 'Give back this job’s credits',\n disabled: row.creditsSpent <= 0,\n disabledReason: row.creditsSpent <= 0 ? 'This job spent no credits.' : undefined,\n onClick: () => onGiveBack(row),\n },\n ]}\n />\n )),\n]\n\n/** The month's tokens, and what each kind of request spends of them (AGL-2937). */\nfunction TokensSection({ tokens }: { tokens: StaffOrgAiTokens }) {\n const { total } = tokens\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Typography variant=\"body2\">\n {`${tokenCount(total.input)} sent · ${tokenCount(total.cached)} read from cache · ${tokenCount(total.cacheWrite)} written to cache · ${tokenCount(total.output)} generated`}\n </Typography>\n <Chip\n size=\"small\"\n color={tokens.cacheHitRate != null && tokens.cacheHitRate >= 0.5 ? 'success' : 'default'}\n label={`cache hit rate ${rate(tokens.cacheHitRate)}`}\n />\n </Stack>\n {tokens.kinds.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No model requests recorded by kind this month.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Kind'}</TableCell>\n <TableCell align=\"right\">{'Requests'}</TableCell>\n <TableCell align=\"right\">{'Per request'}</TableCell>\n <TableCell align=\"right\">{'Sent'}</TableCell>\n <TableCell align=\"right\">{'From cache'}</TableCell>\n <TableCell align=\"right\">{'Generated'}</TableCell>\n <TableCell align=\"right\">{'Cache hit'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {tokens.kinds.map((row) => (\n <TableRow key={row.kind}>\n <TableCell>{row.kind}</TableCell>\n <TableCell align=\"right\">{row.requests.toLocaleString()}</TableCell>\n <TableCell align=\"right\">{usd(row.costPerRequestUsd)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.input)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.cached)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.output)}</TableCell>\n <TableCell align=\"right\">{rate(row.cacheHitRate)}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n </Stack>\n )\n}\n\n/**\n * The month's top spenders, one row per person. The row opens the account;\n * the uid rides beneath the roster's name because two people can share one.\n */\nfunction TopUsersSection({ users }: { users: StaffOrgAiUser[] }) {\n const router = useRouter()\n const columns = useMemo<GridColDef<StaffOrgAiUser>[]>(\n () => [\n {\n field: 'name',\n headerName: 'Person',\n flex: 1,\n minWidth: 220,\n renderCell: ({ row }) => (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap title={row.name}>\n {row.name}\n </Typography>\n {row.name !== row.uid ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n noWrap\n sx={{ fontFamily: 'monospace' }}\n >\n {row.uid}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n {\n field: 'credits',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => credits(value),\n },\n {\n field: 'share',\n headerName: 'Share',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 90,\n valueFormatter: (value: number) => `${Math.round(value * 100)}%`,\n },\n {\n field: 'estCostUsd',\n headerName: 'Provider $',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 120,\n valueFormatter: (value: number) => usd(value),\n },\n {\n field: 'requests',\n headerName: 'Requests',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n {\n field: 'refusals',\n headerName: 'Refusals',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n listActionsColumn((row: StaffOrgAiUser) => (\n <ListRowActions\n label={row.name}\n quick={{\n icon: mdiOpenInNew.path,\n label: 'View account',\n newTab: true,\n to: buildRoute(Route.ADMIN_USER_DETAIL, { uid: row.uid }),\n }}\n items={[]}\n />\n )),\n ],\n [],\n )\n return (\n <ListTable\n rows={users}\n columns={columns}\n getRowId={(row: StaffOrgAiUser) => row.uid}\n rowHeight={TABLE_ROW_HEIGHT}\n onOpen={(uid) => router.push(buildRoute(Route.ADMIN_USER_DETAIL, { uid }))}\n // The route's ten dearest members: a bounded readout, so no filter\n // over the rows it holds (AGL-3321).\n disableColumnFilter\n quickFilter={false}\n />\n )\n}\n\nconst StaffOrgAiCard = ({ orgId }: { orgId: string }) => {\n const { data: user } = useUser()\n // Keyed on who is signed in, not on the user object's identity.\n const signedInUid = user?.uid ?? null\n const userRef = useRef(user)\n userRef.current = user\n const [data, setData] = useState<StaffOrgAiResponse | null>(null)\n const [ready, setReady] = useState(false)\n const [error, setError] = useState<string | null>(null)\n // Bumped after a give-back, so the card re-reads the figures it changed.\n const [version, setVersion] = useState(0)\n const [notice, setNotice] = useState<string | null>(null)\n const [returning, setReturning] = useState<{\n mode: 'give' | 'reset'\n jobId?: string\n jobCredits?: number\n } | null>(null)\n const giveBackJob = useCallback(\n (job: StaffOrgAiJob) =>\n setReturning({ mode: 'give', jobId: job.id, jobCredits: job.creditsSpent }),\n [],\n )\n\n useEffect(() => {\n // Mounted only for a confirmed staff reader — the `staffOrg` zone sits\n // inside the page's `StaffOnly`, which renders nothing while the claim\n // loads — because the route records an access row on every successful\n // open, and an open the page did not mean to make would be an access\n // nobody made.\n const user = userRef.current\n if (!orgId || !signedInUid || !user) return undefined\n let active = true\n setReady(false)\n setError(null)\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/admin/org?orgId=${encodeURIComponent(orgId)}`,\n )\n const payload = await response.json().catch(() => null)\n if (!active) return\n if (!response.ok) {\n setError(payload?.error ?? 'AI lookup failed')\n setData(null)\n } else {\n setData(payload as StaffOrgAiResponse)\n }\n } catch {\n if (active) {\n setError('AI lookup failed')\n setData(null)\n }\n } finally {\n if (active) setReady(true)\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, signedInUid, version])\n\n const name = aiAddonName()\n return (\n <CardDisplay\n header={name}\n help={pluginDocsHelp('aiMonitoring', {\n anchor: '#the-ai-card',\n excerpt:\n '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.',\n })}\n HeaderProps={{\n action: (\n <StaffCreditReturnActions\n disabled={!data}\n onGive={() => setReturning({ mode: 'give' })}\n onReset={() => setReturning({ mode: 'reset' })}\n />\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {data ? (\n <StaffCreditReturnDialog\n open={returning !== null}\n mode={returning?.mode ?? 'give'}\n target={{ orgId }}\n workspace={{ used: data.pool.usedCredits, limit: data.pool.totalCredits }}\n account={\n data.account\n ? { used: data.account.usedCredits, limit: data.account.limitCredits }\n : null\n }\n jobId={returning?.jobId ?? null}\n jobCredits={returning?.jobCredits ?? null}\n onClose={() => setReturning(null)}\n onReturned={(message) => {\n setReturning(null)\n setNotice(message)\n setVersion((current) => current + 1)\n }}\n />\n ) : null}\n {notice ? (\n <Alert severity=\"success\" onClose={() => setNotice(null)} sx={{ mb: 2 }}>\n {notice}\n </Alert>\n ) : null}\n {!ready ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : error || !data ? (\n <Alert severity=\"warning\">\n {`Could not read this organization’s AI usage — ${error ?? 'a failed read'}, not zero usage.`}\n </Alert>\n ) : (\n <Stack spacing={2}>\n {/* Add-on */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Add-on'}\n </Typography>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Chip\n size=\"small\"\n color={data.addon.on ? 'primary' : 'default'}\n label={data.addon.on ? `${name} on` : `${name} off`}\n />\n {data.addon.priceUsd !== null ? (\n <Typography variant=\"body2\">\n {`${usd(data.addon.priceUsd)}/mo on this plan`}\n </Typography>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Not sold on this plan'}\n </Typography>\n )}\n {data.addon.on ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {data.addon.since\n ? `since ${when(data.addon.since)}`\n : data.addon.sinceSource === 'unavailable'\n ? 'since — (Stripe not reachable)'\n : 'since — (no subscription item)'}\n </Typography>\n ) : null}\n </Stack>\n </Stack>\n\n {/* Credit pool */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Credit pool · ${data.month}`}\n </Typography>\n {data.pool.totalCredits === null && data.pool.uncapped ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Uncapped staff comp — no AI credit band. Bounded only by the message cap and an operator’s explicit spend ceiling.'}\n </Typography>\n ) : data.pool.totalCredits === null ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI credit band on this plan — bounded by the message cap and the operator backstop.'}\n </Typography>\n ) : (\n <Typography variant=\"body2\">\n {`${credits(data.pool.totalCredits)} credits = ${poolPartsSentence(data.pool, name)}`}\n </Typography>\n )}\n <Typography variant=\"body2\">\n {`Used ${credits(data.pool.usedCredits)} credits (${usd(data.pool.providerUsd)} provider spend)`}\n {data.pool.remainingCredits !== null\n ? ` · ${credits(data.pool.remainingCredits)} remaining`\n : ''}\n </Typography>\n {data.pool.returnedCredits ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${credits(data.pool.returnedCredits)} credits given back this month — already taken off the figure above.`}\n </Typography>\n ) : null}\n {data.account ? (\n <Typography variant=\"body2\">{allowanceSentence(data.account, 'org')}</Typography>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`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`}\n </Typography>\n </Stack>\n\n {/* Overage */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Overage'}\n </Typography>\n <Typography variant=\"body2\">\n {`${credits(data.overage.overageCredits)} credits over the band · ${usd(data.overage.accruedUsd)} accrued`}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {overageStateSentence(data.overage, data.pool.uncapped === true)}\n </Typography>\n </Stack>\n\n {/* Refusals */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Refusals this month · ${data.refusals.total.toLocaleString()}`}\n </Typography>\n <RefusalsRow refusals={data.refusals} />\n </Stack>\n\n {/* Jobs */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Generation jobs'}\n </Typography>\n <JobsSection jobs={data.jobs} onGiveBack={giveBackJob} />\n </Stack>\n\n {/* Tokens — absent from a route older than the card */}\n {data.tokens ? (\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Tokens · ${data.month}`}\n </Typography>\n <TokensSection tokens={data.tokens} />\n </Stack>\n ) : null}\n\n {/* Top users */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Top users this month'}\n </Typography>\n {data.users.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI usage attributed to a person this month.'}\n </Typography>\n ) : (\n <TopUsersSection users={data.users} />\n )}\n </Stack>\n\n {/* Margin */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Margin'}\n </Typography>\n <Alert severity={marginTone(data.margin)}>\n {`Spend ${usd(data.margin.spendUsd)} against ${usd(data.margin.revenueUsd)} in — ` +\n `${usd(data.margin.addonRevenueUsd)} add-on, ${usd(data.margin.planAssistShareUsd)} plan assist share, ` +\n `${usd(data.margin.overageRevenueUsd)} overage.`}\n {data.margin.underwater\n ? ' Spend exceeds the add-on plus the plan’s assist share.'\n : ''}\n {data.margin.multiple >= 1\n ? ` ${data.margin.multiple}× the ${usd(data.margin.thresholdUsd)} staff review threshold.`\n : ''}\n </Alert>\n {data.margin.contribution.marginPct !== null ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Whole-org contribution margin ${Math.round(data.margin.contribution.marginPct * 100)}% on the ${data.margin.contribution.month} rollup with this month’s AI spend.`}\n </Typography>\n ) : null}\n </Stack>\n\n {/* Actions */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Actions'}\n </Typography>\n <Typography variant=\"body2\">\n {'Credit override and feature toggles: '}\n <AppLink href={buildRoute(Route.ADMIN_ORGS)} underline=\"hover\">\n {'the entitlement override editor'}\n </AppLink>\n {' (also under Staff actions above). Pause AI for this org: '}\n <AppLink href={buildRoute(Route.ADMIN_LOCKDOWN)} underline=\"hover\">\n {'Lockdown'}\n </AppLink>\n {', feature keys ai-assist and ai-generate scoped to the org.'}\n </Typography>\n </Stack>\n </Stack>\n )}\n </CardDisplay>\n )\n}\nStaffOrgAiCard.displayName = 'StaffOrgAiCard'\n\nexport default StaffOrgAiCard\n"],"names":["aiAddonName","buildRoute","Route","pluginDocsHelp","mdiOpenInNew","AppLink","CardDisplay","ListRowActions","ListTable","listActionsColumn","ScrollTable","TABLE_ROW_HEIGHT","useUser","authorizedFetch","Alert","Chip","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useRouter","useCallback","useEffect","useMemo","useRef","useState","allowanceSentence","credits","marginTone","overageStateSentence","poolPartsSentence","rate","refusalChips","tokenCount","usd","JOB_CHIP_STATUSES","StaffCreditReturnActions","StaffCreditReturnDialog","when","iso","Date","toLocaleDateString","RefusalsRow","refusals","direction","spacing","useFlexGap","sx","flexWrap","map","chip","size","variant","count","color","label","toLocaleString","JobsSection","jobs","onGiveBack","columns","jobColumns","severity","total","Object","values","counts","reduce","sum","n","status","replace","done","truncated","rows","recent","getRowId","job","id","rowHeight","noRowsLabel","disableColumnFilter","quickFilter","field","headerName","flex","minWidth","renderCell","row","noWrap","title","fontFamily","width","valueFormatter","value","String","type","align","headerAlign","creditsReserved","createdBy","href","ADMIN_USER_DETAIL","uid","underline","items","key","disabled","creditsSpent","disabledReason","undefined","onClick","TokensSection","tokens","alignItems","input","cached","cacheWrite","output","cacheHitRate","kinds","length","kind","requests","costPerRequestUsd","TopUsersSection","users","router","name","Math","round","quick","icon","path","newTab","to","onOpen","push","StaffOrgAiCard","orgId","data","user","signedInUid","userRef","current","setData","ready","setReady","error","setError","version","setVersion","notice","setNotice","returning","setReturning","giveBackJob","mode","jobId","jobCredits","active","response","encodeURIComponent","payload","json","catch","ok","header","help","anchor","excerpt","HeaderProps","action","onGive","onReset","contentGutterX","contentGutterY","open","target","workspace","used","pool","usedCredits","limit","totalCredits","account","limitCredits","onClose","onReturned","message","mb","addon","on","priceUsd","since","sinceSource","month","uncapped","providerUsd","remainingCredits","returnedCredits","projectedCredits","projectedUsd","messages","deflected","overage","overageCredits","accruedUsd","margin","spendUsd","revenueUsd","addonRevenueUsd","planAssistShareUsd","overageRevenueUsd","underwater","multiple","thresholdUsd","contribution","marginPct","ADMIN_ORGS","ADMIN_LOCKDOWN","displayName"],"mappings":"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"}
@@ -27,6 +27,7 @@ import { buildRoute, Route } from "@aglyn/aglyn/app-utils/console-routes";
27
27
  import { AppLink } from "@aglyn/shared-ui-jsx";
28
28
  import { useCallback, useEffect, useRef, useState } from "react";
29
29
  import { STAFF_AI_CONVERSATIONS_PAGE } from "../usage/staff-org-ai-conversations.js";
30
+ import { jobFilledInText, jobMadeText, jobResultText, money, who } from "../usage/staff-org-ai-conversations-view.js";
30
31
  /**
31
32
  * "Aglyn AI requests" (AGL-3675, AGL-3660): everything people typed to
32
33
  * Aglyn AI and what it answered — the Assist chat's questions (in the console
@@ -40,11 +41,6 @@ import { STAFF_AI_CONVERSATIONS_PAGE } from "../usage/staff-org-ai-conversations
40
41
  * for every page it returns, so a card that loaded with the page would log
41
42
  * reads nobody chose to make.
42
43
  */ const when = (iso)=>iso ? new Date(iso).toLocaleString() : 'Unknown time';
43
- const who = (person)=>{
44
- var _ref, _person_email;
45
- return (_ref = (_person_email = person.email) != null ? _person_email : person.name) != null ? _ref : person.uid ? `uid ${person.uid}` : 'Unknown member';
46
- };
47
- const money = (value)=>value === null ? null : `$${value.toFixed(value < 1 ? 4 : 2)}`;
48
44
  /** A block of what someone typed or what came back, kept as written. */ const Words = ({ label, children })=>/*#__PURE__*/ _jsxs(Box, {
49
45
  children: [
50
46
  /*#__PURE__*/ _jsx(Typography, {
@@ -125,37 +121,11 @@ const AssistRow = ({ row, showOrg })=>/*#__PURE__*/ _jsxs(Stack, {
125
121
  ]
126
122
  });
127
123
  const Result = ({ result })=>{
128
- switch(result.kind){
129
- case 'insight':
130
- return /*#__PURE__*/ _jsx(Words, {
131
- label: "Answer",
132
- children: [
133
- ...result.insights,
134
- ...result.gap ? [
135
- `Not answered: ${result.gap}`
136
- ] : []
137
- ].join('\n\n')
138
- });
139
- case 'crm-record':
140
- return /*#__PURE__*/ _jsx(Words, {
141
- label: "Summary",
142
- children: [
143
- result.summary,
144
- result.standing,
145
- result.nextStep ? `Next step: ${result.nextStep}` : null
146
- ].filter(Boolean).join('\n\n')
147
- });
148
- case 'crm-email':
149
- return /*#__PURE__*/ _jsx(Words, {
150
- label: "Email draft",
151
- children: `Subject: ${result.subject}\n\n${result.body}`
152
- });
153
- case 'crm-mapping':
154
- return /*#__PURE__*/ _jsx(Words, {
155
- label: "Column matches",
156
- children: result.matches.join('\n')
157
- });
158
- }
124
+ const { label, text } = jobResultText(result);
125
+ return /*#__PURE__*/ _jsx(Words, {
126
+ label: label,
127
+ children: text
128
+ });
159
129
  };
160
130
  /** Where a row was asked, on the account card: the org's staff page. */ const OrgChip = ({ orgId })=>orgId ? /*#__PURE__*/ _jsx(AppLink, {
161
131
  href: buildRoute(Route.ADMIN_ORG_DETAIL, {
@@ -210,17 +180,14 @@ const JobRow = ({ row, showOrg })=>{
210
180
  }),
211
181
  ((_row_inputs = row.inputs) == null ? void 0 : _row_inputs.length) ? /*#__PURE__*/ _jsx(Words, {
212
182
  label: "Filled in",
213
- children: row.inputs.map((input)=>`${input.label}: ${input.value}`).join('\n')
183
+ children: jobFilledInText(row)
214
184
  }) : null,
215
185
  row.result ? /*#__PURE__*/ _jsx(Result, {
216
186
  result: row.result
217
187
  }) : null,
218
188
  row.outputs.length ? /*#__PURE__*/ _jsx(Words, {
219
189
  label: "Made",
220
- children: row.outputs.map((output)=>[
221
- output.label,
222
- output.note
223
- ].filter(Boolean).join(' — ')).join('\n')
190
+ children: jobMadeText(row)
224
191
  }) : null,
225
192
  row.error ? /*#__PURE__*/ _jsx(Alert, {
226
193
  severity: "error",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/staff-org-ai-conversations.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Divider,\n Stack,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from 'react'\nimport {\n STAFF_AI_CONVERSATIONS_PAGE,\n type StaffAiAssistRow,\n type StaffAiConversationKind,\n type StaffAiConversationPerson,\n type StaffAiConversationsResponse,\n type StaffAiJobResult,\n type StaffAiJobRow,\n} from '../usage/staff-org-ai-conversations'\n\n/**\n * \"Aglyn AI requests\" (AGL-3675, AGL-3660): everything people typed to\n * Aglyn AI and what it answered — the Assist chat's questions (in the console\n * and the Besigner) and answers, and each AI job's brief, the fields filled\n * in beside it (a guided start's form, a Create-with-AI dialog's options)\n * and its result — newest first. On the staff org page for one organization,\n * and on the staff account page for one account across every organization\n * it asked in.\n *\n * Nothing is read until staff ask: the route writes an audited access row\n * for every page it returns, so a card that loaded with the page would log\n * reads nobody chose to make.\n */\n\nconst when = (iso: string | null) => (iso ? new Date(iso).toLocaleString() : 'Unknown time')\n\nconst who = (person: StaffAiConversationPerson) =>\n person.email ?? person.name ?? (person.uid ? `uid ${person.uid}` : 'Unknown member')\n\nconst money = (value: number | null) => (value === null ? null : `$${value.toFixed(value < 1 ? 4 : 2)}`)\n\n/** A block of what someone typed or what came back, kept as written. */\nconst Words = ({ label, children }: { label: string; children: string }) => (\n <Box>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {label}\n </Typography>\n <Typography variant=\"body2\" sx={{ whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }}>\n {children || '—'}\n </Typography>\n </Box>\n)\n\nconst Meta = ({ children }: { children: ReactNode }) => (\n <Stack direction=\"row\" useFlexGap sx={{ flexWrap: 'wrap', gap: 1, alignItems: 'center' }}>\n {children}\n </Stack>\n)\n\nconst AssistRow = ({ row, showOrg }: { row: StaffAiAssistRow; showOrg?: boolean }) => (\n <Stack spacing={1}>\n <Meta>\n {showOrg ? <OrgChip orgId={row.orgId} /> : null}\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {who(row.by)}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {when(row.at)}\n </Typography>\n {row.route ? <Chip size=\"small\" variant=\"outlined\" label={row.route} /> : null}\n {row.model ? <Chip size=\"small\" variant=\"outlined\" label={row.model} /> : null}\n {money(row.estCostUsd) ? <Chip size=\"small\" variant=\"outlined\" label={money(row.estCostUsd)} /> : null}\n {row.feedback ? (\n <Chip size=\"small\" color={row.feedback === 'up' ? 'success' : 'warning'} label={`Rated ${row.feedback}`} />\n ) : null}\n </Meta>\n <Words label=\"Question\">{row.question}</Words>\n <Words label=\"Answer\">{row.answer}</Words>\n </Stack>\n)\n\nconst Result = ({ result }: { result: StaffAiJobResult }) => {\n switch (result.kind) {\n case 'insight':\n return (\n <Words label=\"Answer\">\n {[...result.insights, ...(result.gap ? [`Not answered: ${result.gap}`] : [])].join('\\n\\n')}\n </Words>\n )\n case 'crm-record':\n return (\n <Words label=\"Summary\">\n {[result.summary, result.standing, result.nextStep ? `Next step: ${result.nextStep}` : null]\n .filter(Boolean)\n .join('\\n\\n')}\n </Words>\n )\n case 'crm-email':\n return <Words label=\"Email draft\">{`Subject: ${result.subject}\\n\\n${result.body}`}</Words>\n case 'crm-mapping':\n return <Words label=\"Column matches\">{result.matches.join('\\n')}</Words>\n }\n}\n\n/** Where a row was asked, on the account card: the org's staff page. */\nconst OrgChip = ({ orgId }: { orgId: string | null }) =>\n orgId ? (\n <AppLink\n href={buildRoute(Route.ADMIN_ORG_DETAIL, { orgId })}\n variant=\"body2\"\n underline=\"hover\"\n >\n {`Organization ${orgId.slice(0, 8)}…`}\n </AppLink>\n ) : null\n\nconst JobRow = ({ row, showOrg }: { row: StaffAiJobRow; showOrg?: boolean }) => (\n <Stack spacing={1}>\n <Meta>\n {showOrg ? <OrgChip orgId={row.orgId} /> : null}\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {who(row.by)}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {when(row.at)}\n </Typography>\n <Chip size=\"small\" variant=\"outlined\" label={row.kind} />\n <Chip\n size=\"small\"\n color={row.status === 'done' ? 'success' : row.status === 'failed' ? 'error' : 'default'}\n label={row.status}\n />\n <Chip size=\"small\" variant=\"outlined\" label={`${row.credits.toLocaleString()} credits`} />\n </Meta>\n <Words label=\"Brief\">{row.brief}</Words>\n {row.inputs?.length ? (\n <Words label=\"Filled in\">\n {row.inputs.map((input) => `${input.label}: ${input.value}`).join('\\n')}\n </Words>\n ) : null}\n {row.result ? <Result result={row.result} /> : null}\n {row.outputs.length ? (\n <Words label=\"Made\">\n {row.outputs\n .map((output) => [output.label, output.note].filter(Boolean).join(' — '))\n .join('\\n')}\n </Words>\n ) : null}\n {row.error ? <Alert severity=\"error\">{row.error}</Alert> : null}\n </Stack>\n)\n\n/** The anchor an activity row's \"Open the AI job\" link lands on. */\nexport const STAFF_AI_REQUESTS_ANCHOR = 'aglyn-ai-requests'\n\nexport interface StaffAiRequestsProps {\n /** The staff org page: this organization's requests. */\n orgId?: string\n /** The staff account page: this account's requests, in every org. */\n uid?: string\n}\n\nconst StaffOrgAiConversations = ({ orgId, uid }: StaffAiRequestsProps) => {\n const subject = uid ? { uid } : orgId ? { orgId } : null\n const account = Boolean(uid)\n // `?aiJob=` — an activity or audit row's link to the job behind it opens\n // the card on that one job; asking for it IS the staff member's ask.\n // Read from the location in an effect rather than `useSearchParams`, which\n // would ask every page this zone sits on for a Suspense boundary.\n const [focusJob, setFocusJob] = useState<string | null>(null)\n useEffect(() => {\n setFocusJob(new URLSearchParams(window.location.search).get('aiJob'))\n }, [])\n const [focused, setFocused] = useState<string | null>(null)\n const focusOpened = useRef(false)\n const { data: user } = useUser()\n const userRef = useRef(user)\n userRef.current = user\n const [kind, setKind] = useState<StaffAiConversationKind | null>(null)\n const [rows, setRows] = useState<Array<StaffAiAssistRow | StaffAiJobRow>>([])\n // The cursor that opens each page: `cursors[page]` is the `after` its\n // request carries, so Previous re-reads a page rather than keeping every\n // page read so far in memory.\n const [cursors, setCursors] = useState<Array<string | null>>([null])\n const [page, setPage] = useState(0)\n const [next, setNext] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const load = useCallback(\n async (\n target: StaffAiConversationKind,\n targetPage: number,\n after: string | null,\n jobId: string | null = null,\n ) => {\n const current = userRef.current\n if (!current || !subject) return\n setLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ ...subject, kind: target })\n if (after) params.set('after', after)\n if (jobId) params.set('jobId', jobId)\n const response = await authorizedFetch(current, `/api/ai/admin/conversations?${params}`)\n const payload = (await response.json().catch(() => null)) as\n | (StaffAiConversationsResponse & { error?: string })\n | null\n if (!response.ok || !payload) {\n setError(payload?.error ?? 'The conversations could not be read.')\n return\n }\n setRows(payload.rows)\n setPage(targetPage)\n setNext(payload.next)\n setCursors((existing) => {\n const kept = existing.slice(0, targetPage + 1)\n return payload.next ? [...kept, payload.next] : kept\n })\n } catch {\n setError('The conversations could not be read.')\n } finally {\n setLoading(false)\n }\n },\n // `subject` is rebuilt from these two every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [orgId, uid],\n )\n\n const show = useCallback(\n (target: StaffAiConversationKind) => {\n setFocused(null)\n setKind(target)\n setRows([])\n setNext(null)\n setPage(0)\n setCursors([null])\n void load(target, 0, null)\n },\n [load],\n )\n\n const signedIn = Boolean(user)\n useEffect(() => {\n // Once per visit: Show all jobs clears the focus and must not bring it back.\n if (account || !focusJob || !signedIn || focusOpened.current) return\n focusOpened.current = true\n setFocused(focusJob)\n setKind('jobs')\n setRows([])\n setNext(null)\n setPage(0)\n setCursors([null])\n void load('jobs', 0, null, focusJob)\n // The card mounts after the page's own hash scroll has come and gone.\n document.getElementById(STAFF_AI_REQUESTS_ANCHOR)?.scrollIntoView({ block: 'start' })\n }, [account, focusJob, signedIn, load])\n\n return (\n <Box id={STAFF_AI_REQUESTS_ANCHOR} sx={{ scrollMarginTop: 80 }}>\n <CardDisplay\n header={`${PLATFORM_BRAND_NAME} AI requests`}\n help={pluginDocsHelp('aiMonitoring', {\n anchor: '#ai-conversations',\n excerpt:\n `What ${account ? 'this account' : 'this organization'} typed to ${PLATFORM_BRAND_NAME} AI and what it answered: Assist chat questions and answers, and each AI job’s brief, the fields filled in beside it, and its result. Each page staff read is recorded in the audit log, without the text.`,\n })}\n HeaderProps={{\n action:\n kind === null ? (\n <Button size=\"small\" onClick={() => show('assist')}>\n {'Show requests'}\n </Button>\n ) : (\n <ToggleButtonGroup\n size=\"small\"\n exclusive\n value={kind}\n onChange={(_, value: StaffAiConversationKind | null) => value && show(value)}\n aria-label=\"Which conversations\"\n >\n <ToggleButton value=\"assist\">{'Assist chat'}</ToggleButton>\n <ToggleButton value=\"jobs\">{'AI jobs'}</ToggleButton>\n </ToggleButtonGroup>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {kind === null ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {account\n ? `What this account typed to ${PLATFORM_BRAND_NAME} AI in every organization, and what it answered. Opening it is recorded in the audit log, and in this account's data access by staff.`\n : `What people in this organization typed to ${PLATFORM_BRAND_NAME} AI, and what it answered. Opening it is recorded in the audit log.`}\n </Typography>\n ) : (\n <Stack spacing={2} divider={<Divider flexItem />}>\n {focused ? (\n <Alert\n severity=\"info\"\n action={\n <Button size=\"small\" onClick={() => show('jobs')}>\n {'Show all jobs'}\n </Button>\n }\n >\n {'Showing the one AI job the link pointed at.'}\n </Alert>\n ) : null}\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n {!loading && !error && !rows.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {kind === 'assist'\n ? 'No Assist chat in the last 180 days.'\n : 'No AI jobs yet.'}\n </Typography>\n ) : null}\n {rows.map((row) =>\n kind === 'assist' ? (\n <AssistRow key={`${row.orgId}:${row.id}`} row={row as StaffAiAssistRow} showOrg={account} />\n ) : (\n <JobRow key={`${row.orgId}:${row.id}`} row={row as StaffAiJobRow} showOrg={account} />\n ),\n )}\n {loading ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : null}\n {rows.length || page > 0 ? (\n <ListPagination\n page={page}\n pageSize={STAFF_AI_CONVERSATIONS_PAGE}\n rowCount={rows.length}\n hasMore={Boolean(next)}\n disabled={loading}\n onPageChange={(target) => void load(kind, target, cursors[target] ?? null)}\n />\n ) : null}\n </Stack>\n )}\n </CardDisplay>\n </Box>\n )\n}\n\nStaffOrgAiConversations.displayName = 'StaffOrgAiConversations'\n\nexport default StaffOrgAiConversations\n"],"names":["PLATFORM_BRAND_NAME","pluginDocsHelp","CardDisplay","ListPagination","authorizedFetch","useUser","Alert","Box","Button","Chip","Divider","Stack","ToggleButton","ToggleButtonGroup","Typography","buildRoute","Route","AppLink","useCallback","useEffect","useRef","useState","STAFF_AI_CONVERSATIONS_PAGE","when","iso","Date","toLocaleString","who","person","email","name","uid","money","value","toFixed","Words","label","children","variant","color","sx","whiteSpace","overflowWrap","Meta","direction","useFlexGap","flexWrap","gap","alignItems","AssistRow","row","showOrg","spacing","OrgChip","orgId","fontWeight","by","at","route","size","model","estCostUsd","feedback","question","answer","Result","result","kind","insights","join","summary","standing","nextStep","filter","Boolean","subject","body","matches","href","ADMIN_ORG_DETAIL","underline","slice","JobRow","status","credits","brief","inputs","length","map","input","outputs","output","note","error","severity","STAFF_AI_REQUESTS_ANCHOR","StaffOrgAiConversations","account","focusJob","setFocusJob","URLSearchParams","window","location","search","get","focused","setFocused","focusOpened","data","user","userRef","current","setKind","rows","setRows","cursors","setCursors","page","setPage","next","setNext","loading","setLoading","setError","load","target","targetPage","after","jobId","params","set","response","payload","json","catch","ok","existing","kept","show","signedIn","document","getElementById","scrollIntoView","block","id","scrollMarginTop","header","help","anchor","excerpt","HeaderProps","action","onClick","exclusive","onChange","_","aria-label","contentGutterX","contentGutterY","divider","flexItem","pageSize","rowCount","hasMore","disabled","onPageChange","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/staff-org-ai-conversations.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Divider,\n Stack,\n ToggleButton,\n ToggleButtonGroup,\n Typography,\n} from '@mui/material'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from 'react'\nimport {\n STAFF_AI_CONVERSATIONS_PAGE,\n type StaffAiAssistRow,\n type StaffAiConversationKind,\n type StaffAiConversationsResponse,\n type StaffAiJobResult,\n type StaffAiJobRow,\n} from '../usage/staff-org-ai-conversations'\nimport { jobFilledInText, jobMadeText, jobResultText, money, who } from '../usage/staff-org-ai-conversations-view'\n\n/**\n * \"Aglyn AI requests\" (AGL-3675, AGL-3660): everything people typed to\n * Aglyn AI and what it answered — the Assist chat's questions (in the console\n * and the Besigner) and answers, and each AI job's brief, the fields filled\n * in beside it (a guided start's form, a Create-with-AI dialog's options)\n * and its result — newest first. On the staff org page for one organization,\n * and on the staff account page for one account across every organization\n * it asked in.\n *\n * Nothing is read until staff ask: the route writes an audited access row\n * for every page it returns, so a card that loaded with the page would log\n * reads nobody chose to make.\n */\n\nconst when = (iso: string | null) => (iso ? new Date(iso).toLocaleString() : 'Unknown time')\n\n/** A block of what someone typed or what came back, kept as written. */\nconst Words = ({ label, children }: { label: string; children: string }) => (\n <Box>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {label}\n </Typography>\n <Typography variant=\"body2\" sx={{ whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }}>\n {children || '—'}\n </Typography>\n </Box>\n)\n\nconst Meta = ({ children }: { children: ReactNode }) => (\n <Stack direction=\"row\" useFlexGap sx={{ flexWrap: 'wrap', gap: 1, alignItems: 'center' }}>\n {children}\n </Stack>\n)\n\nconst AssistRow = ({ row, showOrg }: { row: StaffAiAssistRow; showOrg?: boolean }) => (\n <Stack spacing={1}>\n <Meta>\n {showOrg ? <OrgChip orgId={row.orgId} /> : null}\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {who(row.by)}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {when(row.at)}\n </Typography>\n {row.route ? <Chip size=\"small\" variant=\"outlined\" label={row.route} /> : null}\n {row.model ? <Chip size=\"small\" variant=\"outlined\" label={row.model} /> : null}\n {money(row.estCostUsd) ? <Chip size=\"small\" variant=\"outlined\" label={money(row.estCostUsd)} /> : null}\n {row.feedback ? (\n <Chip size=\"small\" color={row.feedback === 'up' ? 'success' : 'warning'} label={`Rated ${row.feedback}`} />\n ) : null}\n </Meta>\n <Words label=\"Question\">{row.question}</Words>\n <Words label=\"Answer\">{row.answer}</Words>\n </Stack>\n)\n\nconst Result = ({ result }: { result: StaffAiJobResult }) => {\n const { label, text } = jobResultText(result)\n return <Words label={label}>{text}</Words>\n}\n\n/** Where a row was asked, on the account card: the org's staff page. */\nconst OrgChip = ({ orgId }: { orgId: string | null }) =>\n orgId ? (\n <AppLink\n href={buildRoute(Route.ADMIN_ORG_DETAIL, { orgId })}\n variant=\"body2\"\n underline=\"hover\"\n >\n {`Organization ${orgId.slice(0, 8)}…`}\n </AppLink>\n ) : null\n\nconst JobRow = ({ row, showOrg }: { row: StaffAiJobRow; showOrg?: boolean }) => (\n <Stack spacing={1}>\n <Meta>\n {showOrg ? <OrgChip orgId={row.orgId} /> : null}\n <Typography variant=\"body2\" sx={{ fontWeight: 600 }}>\n {who(row.by)}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {when(row.at)}\n </Typography>\n <Chip size=\"small\" variant=\"outlined\" label={row.kind} />\n <Chip\n size=\"small\"\n color={row.status === 'done' ? 'success' : row.status === 'failed' ? 'error' : 'default'}\n label={row.status}\n />\n <Chip size=\"small\" variant=\"outlined\" label={`${row.credits.toLocaleString()} credits`} />\n </Meta>\n <Words label=\"Brief\">{row.brief}</Words>\n {row.inputs?.length ? <Words label=\"Filled in\">{jobFilledInText(row)}</Words> : null}\n {row.result ? <Result result={row.result} /> : null}\n {row.outputs.length ? <Words label=\"Made\">{jobMadeText(row)}</Words> : null}\n {row.error ? <Alert severity=\"error\">{row.error}</Alert> : null}\n </Stack>\n)\n\n/** The anchor an activity row's \"Open the AI job\" link lands on. */\nexport const STAFF_AI_REQUESTS_ANCHOR = 'aglyn-ai-requests'\n\nexport interface StaffAiRequestsProps {\n /** The staff org page: this organization's requests. */\n orgId?: string\n /** The staff account page: this account's requests, in every org. */\n uid?: string\n}\n\nconst StaffOrgAiConversations = ({ orgId, uid }: StaffAiRequestsProps) => {\n const subject = uid ? { uid } : orgId ? { orgId } : null\n const account = Boolean(uid)\n // `?aiJob=` — an activity or audit row's link to the job behind it opens\n // the card on that one job; asking for it IS the staff member's ask.\n // Read from the location in an effect rather than `useSearchParams`, which\n // would ask every page this zone sits on for a Suspense boundary.\n const [focusJob, setFocusJob] = useState<string | null>(null)\n useEffect(() => {\n setFocusJob(new URLSearchParams(window.location.search).get('aiJob'))\n }, [])\n const [focused, setFocused] = useState<string | null>(null)\n const focusOpened = useRef(false)\n const { data: user } = useUser()\n const userRef = useRef(user)\n userRef.current = user\n const [kind, setKind] = useState<StaffAiConversationKind | null>(null)\n const [rows, setRows] = useState<Array<StaffAiAssistRow | StaffAiJobRow>>([])\n // The cursor that opens each page: `cursors[page]` is the `after` its\n // request carries, so Previous re-reads a page rather than keeping every\n // page read so far in memory.\n const [cursors, setCursors] = useState<Array<string | null>>([null])\n const [page, setPage] = useState(0)\n const [next, setNext] = useState<string | null>(null)\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const load = useCallback(\n async (\n target: StaffAiConversationKind,\n targetPage: number,\n after: string | null,\n jobId: string | null = null,\n ) => {\n const current = userRef.current\n if (!current || !subject) return\n setLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ ...subject, kind: target })\n if (after) params.set('after', after)\n if (jobId) params.set('jobId', jobId)\n const response = await authorizedFetch(current, `/api/ai/admin/conversations?${params}`)\n const payload = (await response.json().catch(() => null)) as\n | (StaffAiConversationsResponse & { error?: string })\n | null\n if (!response.ok || !payload) {\n setError(payload?.error ?? 'The conversations could not be read.')\n return\n }\n setRows(payload.rows)\n setPage(targetPage)\n setNext(payload.next)\n setCursors((existing) => {\n const kept = existing.slice(0, targetPage + 1)\n return payload.next ? [...kept, payload.next] : kept\n })\n } catch {\n setError('The conversations could not be read.')\n } finally {\n setLoading(false)\n }\n },\n // `subject` is rebuilt from these two every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [orgId, uid],\n )\n\n const show = useCallback(\n (target: StaffAiConversationKind) => {\n setFocused(null)\n setKind(target)\n setRows([])\n setNext(null)\n setPage(0)\n setCursors([null])\n void load(target, 0, null)\n },\n [load],\n )\n\n const signedIn = Boolean(user)\n useEffect(() => {\n // Once per visit: Show all jobs clears the focus and must not bring it back.\n if (account || !focusJob || !signedIn || focusOpened.current) return\n focusOpened.current = true\n setFocused(focusJob)\n setKind('jobs')\n setRows([])\n setNext(null)\n setPage(0)\n setCursors([null])\n void load('jobs', 0, null, focusJob)\n // The card mounts after the page's own hash scroll has come and gone.\n document.getElementById(STAFF_AI_REQUESTS_ANCHOR)?.scrollIntoView({ block: 'start' })\n }, [account, focusJob, signedIn, load])\n\n return (\n <Box id={STAFF_AI_REQUESTS_ANCHOR} sx={{ scrollMarginTop: 80 }}>\n <CardDisplay\n header={`${PLATFORM_BRAND_NAME} AI requests`}\n help={pluginDocsHelp('aiMonitoring', {\n anchor: '#ai-conversations',\n excerpt:\n `What ${account ? 'this account' : 'this organization'} typed to ${PLATFORM_BRAND_NAME} AI and what it answered: Assist chat questions and answers, and each AI job’s brief, the fields filled in beside it, and its result. Each page staff read is recorded in the audit log, without the text.`,\n })}\n HeaderProps={{\n action:\n kind === null ? (\n <Button size=\"small\" onClick={() => show('assist')}>\n {'Show requests'}\n </Button>\n ) : (\n <ToggleButtonGroup\n size=\"small\"\n exclusive\n value={kind}\n onChange={(_, value: StaffAiConversationKind | null) => value && show(value)}\n aria-label=\"Which conversations\"\n >\n <ToggleButton value=\"assist\">{'Assist chat'}</ToggleButton>\n <ToggleButton value=\"jobs\">{'AI jobs'}</ToggleButton>\n </ToggleButtonGroup>\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {kind === null ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {account\n ? `What this account typed to ${PLATFORM_BRAND_NAME} AI in every organization, and what it answered. Opening it is recorded in the audit log, and in this account's data access by staff.`\n : `What people in this organization typed to ${PLATFORM_BRAND_NAME} AI, and what it answered. Opening it is recorded in the audit log.`}\n </Typography>\n ) : (\n <Stack spacing={2} divider={<Divider flexItem />}>\n {focused ? (\n <Alert\n severity=\"info\"\n action={\n <Button size=\"small\" onClick={() => show('jobs')}>\n {'Show all jobs'}\n </Button>\n }\n >\n {'Showing the one AI job the link pointed at.'}\n </Alert>\n ) : null}\n {error ? <Alert severity=\"warning\">{error}</Alert> : null}\n {!loading && !error && !rows.length ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {kind === 'assist'\n ? 'No Assist chat in the last 180 days.'\n : 'No AI jobs yet.'}\n </Typography>\n ) : null}\n {rows.map((row) =>\n kind === 'assist' ? (\n <AssistRow key={`${row.orgId}:${row.id}`} row={row as StaffAiAssistRow} showOrg={account} />\n ) : (\n <JobRow key={`${row.orgId}:${row.id}`} row={row as StaffAiJobRow} showOrg={account} />\n ),\n )}\n {loading ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : null}\n {rows.length || page > 0 ? (\n <ListPagination\n page={page}\n pageSize={STAFF_AI_CONVERSATIONS_PAGE}\n rowCount={rows.length}\n hasMore={Boolean(next)}\n disabled={loading}\n onPageChange={(target) => void load(kind, target, cursors[target] ?? null)}\n />\n ) : null}\n </Stack>\n )}\n </CardDisplay>\n </Box>\n )\n}\n\nStaffOrgAiConversations.displayName = 'StaffOrgAiConversations'\n\nexport default StaffOrgAiConversations\n"],"names":["PLATFORM_BRAND_NAME","pluginDocsHelp","CardDisplay","ListPagination","authorizedFetch","useUser","Alert","Box","Button","Chip","Divider","Stack","ToggleButton","ToggleButtonGroup","Typography","buildRoute","Route","AppLink","useCallback","useEffect","useRef","useState","STAFF_AI_CONVERSATIONS_PAGE","jobFilledInText","jobMadeText","jobResultText","money","who","when","iso","Date","toLocaleString","Words","label","children","variant","color","sx","whiteSpace","overflowWrap","Meta","direction","useFlexGap","flexWrap","gap","alignItems","AssistRow","row","showOrg","spacing","OrgChip","orgId","fontWeight","by","at","route","size","model","estCostUsd","feedback","question","answer","Result","result","text","href","ADMIN_ORG_DETAIL","underline","slice","JobRow","kind","status","credits","brief","inputs","length","outputs","error","severity","STAFF_AI_REQUESTS_ANCHOR","StaffOrgAiConversations","uid","subject","account","Boolean","focusJob","setFocusJob","URLSearchParams","window","location","search","get","focused","setFocused","focusOpened","data","user","userRef","current","setKind","rows","setRows","cursors","setCursors","page","setPage","next","setNext","loading","setLoading","setError","load","target","targetPage","after","jobId","params","set","response","payload","json","catch","ok","existing","kept","show","signedIn","document","getElementById","scrollIntoView","block","id","scrollMarginTop","header","help","anchor","excerpt","HeaderProps","action","onClick","exclusive","value","onChange","_","aria-label","contentGutterX","contentGutterY","divider","flexItem","map","pageSize","rowCount","hasMore","disabled","onPageChange","displayName"],"mappings":"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"}
@@ -28,6 +28,7 @@ import { Alert, Typography } from "@mui/material";
28
28
  import { useRouter } from "next/navigation";
29
29
  import { useEffect, useRef, useState } from "react";
30
30
  import { aiUsageMonthLabel } from "../usage/ai-usage-wire.js";
31
+ import { allowanceSentence } from "../usage/staff-org-ai-view.js";
31
32
  import { StaffCreditReturnActions, StaffCreditReturnDialog } from "./staff-ai-credit-return.component.js";
32
33
  const orgHref = (orgId)=>buildRoute(Route.ADMIN_ORG_DETAIL, {
33
34
  orgId
@@ -208,7 +209,7 @@ const orgHref = (orgId)=>buildRoute(Route.ADMIN_ORG_DETAIL, {
208
209
  sx: {
209
210
  mb: 1
210
211
  },
211
- children: `Free AI allowance this month: ${allowance.usedCredits.toLocaleString()} of ${allowance.limitCredits.toLocaleString()} credits used across the free workspaces this account owns` + (allowance.returnedCredits ? ` (${allowance.returnedCredits.toLocaleString()} given back)` : '') + '.'
212
+ children: allowanceSentence(allowance, 'user')
212
213
  }) : null,
213
214
  !ready ? /*#__PURE__*/ _jsx(Typography, {
214
215
  variant: "body2",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/staff-user-ai-usage-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { aiAddonName } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiOpenInNew } from '@aglyn/shared-data-mdi'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { Alert, Typography } from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useRouter } from 'next/navigation'\nimport { useEffect, useRef, useState } from 'react'\nimport { aiUsageMonthLabel } from '../usage/ai-usage-wire'\nimport type { StaffAiAccountAllowance } from '../usage/staff-org-ai'\nimport {\n StaffCreditReturnActions,\n StaffCreditReturnDialog,\n} from './staff-ai-credit-return.component'\n\n/** One row, as `/api/ai/admin/user` answers it. */\nexport interface StaffUserAiUsageRow {\n orgId: string\n orgName: string | null\n slug: string | null\n month: string\n credits: number\n requests: number\n refusals: number\n}\n\nconst orgHref = (orgId: string) => buildRoute(Route.ADMIN_ORG_DETAIL, { orgId })\n\n/** A row's id: one workspace's one month. */\nconst rowId = (row: StaffUserAiUsageRow) => `${row.orgId}:${row.month}`\n\n/**\n * One row per workspace per month kept. The row opens the workspace; the\n * month sorts by its key, which orders chronologically.\n */\nconst COLUMNS: GridColDef<StaffUserAiUsageRow>[] = [\n {\n field: 'orgName',\n headerName: 'Organization',\n flex: 1,\n minWidth: 200,\n valueGetter: (_value, row) => row.orgName ?? row.orgId,\n },\n {\n field: 'month',\n headerName: 'Month',\n width: 140,\n valueFormatter: (value: string) => aiUsageMonthLabel(value),\n },\n {\n field: 'credits',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n {\n field: 'requests',\n headerName: 'Requests',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n {\n field: 'refusals',\n headerName: 'Refusals',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n listActionsColumn((row: StaffUserAiUsageRow) => (\n <ListRowActions\n label={row.orgName ?? row.orgId}\n quick={{ icon: mdiOpenInNew.path, label: 'View organization', to: orgHref(row.orgId), newTab: true }}\n items={[]}\n />\n )),\n]\n\n/**\n * ONE ACCOUNT'S AI USAGE ACROSS ORGANIZATIONS (AGL-2928), on the staff user\n * page: workspace, month, credits — every month kept, newest first.\n *\n * Read on mount, and it mounts only for a confirmed staff reader: the\n * `staffUser` zone sits inside the page's `StaffOnly`, which renders nothing\n * while the claim is still loading. The route records an access row about\n * this person on every open, so an open the page did not mean to make would\n * be an access nobody made.\n *\n * Above the table, the account's own Free allowance this month — what every\n * free workspace it owns draws on — and in the header the two acts that give\n * credits back to it (AGL-3595).\n */\nconst StaffUserAiUsageCard = ({ uid }: { uid: string }) => {\n const { data: user } = useUser()\n // Keyed on who is signed in, not on the user object's identity (AGL-2928).\n const signedInUid = user?.uid ?? null\n const userRef = useRef(user)\n userRef.current = user\n const [rows, setRows] = useState<StaffUserAiUsageRow[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [ready, setReady] = useState(false)\n const [allowance, setAllowance] = useState<StaffAiAccountAllowance | null>(null)\n // Bumped after a give-back, so the card re-reads the allowance it changed.\n const [version, setVersion] = useState(0)\n const [notice, setNotice] = useState<string | null>(null)\n const [returning, setReturning] = useState<'give' | 'reset' | null>(null)\n const router = useRouter()\n\n useEffect(() => {\n const user = userRef.current\n if (!uid || !signedInUid || !user) return undefined\n let active = true\n setReady(false)\n setError(null)\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/admin/user?uid=${encodeURIComponent(uid)}`,\n )\n const payload = await response.json().catch(() => null)\n if (!active) return\n if (!response.ok) {\n setError(payload?.error ?? 'AI usage lookup failed')\n setRows(null)\n } else {\n setRows((payload?.rows ?? []) as StaffUserAiUsageRow[])\n // Absent from a route older than give-backs.\n setAllowance((payload?.allowance ?? null) as StaffAiAccountAllowance | null)\n }\n } catch {\n if (active) {\n setError('AI usage lookup failed')\n setRows(null)\n }\n } finally {\n if (active) setReady(true)\n }\n })()\n return () => {\n active = false\n }\n }, [uid, signedInUid, version])\n\n return (\n <CardDisplay\n header={`${aiAddonName()} usage across organizations`}\n help={pluginDocsHelp('aiMonitoring', {\n anchor: '#one-account',\n excerpt:\n 'This account’s AI credits in every workspace it belongs to, month by month. Opening it is recorded as a staff access about this person.',\n })}\n HeaderProps={{\n action: (\n <StaffCreditReturnActions\n disabled={!allowance}\n onGive={() => setReturning('give')}\n onReset={() => setReturning('reset')}\n />\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {allowance ? (\n <StaffCreditReturnDialog\n open={returning !== null}\n mode={returning ?? 'give'}\n target={{ uid }}\n workspace={null}\n account={{ used: allowance.usedCredits, limit: allowance.limitCredits }}\n onClose={() => setReturning(null)}\n onReturned={(message) => {\n setReturning(null)\n setNotice(message)\n setVersion((current) => current + 1)\n }}\n />\n ) : null}\n {notice ? (\n <Alert severity=\"success\" onClose={() => setNotice(null)} sx={{ mb: 2 }}>\n {notice}\n </Alert>\n ) : null}\n {allowance ? (\n <Typography variant=\"body2\" sx={{ mb: 1 }}>\n {`Free AI allowance this month: ${allowance.usedCredits.toLocaleString()} of ${allowance.limitCredits.toLocaleString()} credits used across the free workspaces this account owns` +\n (allowance.returnedCredits\n ? ` (${allowance.returnedCredits.toLocaleString()} given back)`\n : '') +\n '.'}\n </Typography>\n ) : null}\n {!ready ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : error || !rows ? (\n <Alert severity=\"warning\">\n {`Could not read this account’s AI usage — ${error ?? 'a failed read'}, not zero usage.`}\n </Alert>\n ) : rows.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI usage attributed to this account in any workspace it belongs to.'}\n </Typography>\n ) : (\n // Every month kept is in hand — at most fifty workspaces times the\n // retention window — so the grid pages and sorts the one read, and\n // offers no filter or search: a bounded readout is read whole, not\n // narrowed over the rows it holds (AGL-3321).\n <ListTable\n rows={rows}\n columns={COLUMNS}\n getRowId={rowId}\n rowHeight={TABLE_ROW_HEIGHT}\n onOpen={(_id, row: StaffUserAiUsageRow) => router.push(orgHref(row.orgId))}\n disableColumnFilter\n quickFilter={false}\n />\n )}\n </CardDisplay>\n )\n}\nStaffUserAiUsageCard.displayName = 'StaffUserAiUsageCard'\n\nexport default StaffUserAiUsageCard\n"],"names":["aiAddonName","buildRoute","Route","pluginDocsHelp","mdiOpenInNew","CardDisplay","ListRowActions","ListTable","listActionsColumn","TABLE_ROW_HEIGHT","authorizedFetch","useUser","Alert","Typography","useRouter","useEffect","useRef","useState","aiUsageMonthLabel","StaffCreditReturnActions","StaffCreditReturnDialog","orgHref","orgId","ADMIN_ORG_DETAIL","rowId","row","month","COLUMNS","field","headerName","flex","minWidth","valueGetter","_value","orgName","width","valueFormatter","value","type","align","headerAlign","toLocaleString","label","quick","icon","path","to","newTab","items","StaffUserAiUsageCard","uid","data","user","signedInUid","userRef","current","rows","setRows","error","setError","ready","setReady","allowance","setAllowance","version","setVersion","notice","setNotice","returning","setReturning","router","undefined","active","response","encodeURIComponent","payload","json","catch","ok","header","help","anchor","excerpt","HeaderProps","action","disabled","onGive","onReset","contentGutterX","contentGutterY","open","mode","target","workspace","account","used","usedCredits","limit","limitCredits","onClose","onReturned","message","severity","sx","mb","variant","returnedCredits","color","length","columns","getRowId","rowHeight","onOpen","_id","push","disableColumnFilter","quickFilter","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/staff-user-ai-usage-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { aiAddonName } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiOpenInNew } from '@aglyn/shared-data-mdi'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { Alert, Typography } from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useRouter } from 'next/navigation'\nimport { useEffect, useRef, useState } from 'react'\nimport { aiUsageMonthLabel } from '../usage/ai-usage-wire'\nimport type { StaffAiAccountAllowance } from '../usage/staff-org-ai'\nimport { allowanceSentence } from '../usage/staff-org-ai-view'\nimport {\n StaffCreditReturnActions,\n StaffCreditReturnDialog,\n} from './staff-ai-credit-return.component'\n\n/** One row, as `/api/ai/admin/user` answers it. */\nexport interface StaffUserAiUsageRow {\n orgId: string\n orgName: string | null\n slug: string | null\n month: string\n credits: number\n requests: number\n refusals: number\n}\n\nconst orgHref = (orgId: string) => buildRoute(Route.ADMIN_ORG_DETAIL, { orgId })\n\n/** A row's id: one workspace's one month. */\nconst rowId = (row: StaffUserAiUsageRow) => `${row.orgId}:${row.month}`\n\n/**\n * One row per workspace per month kept. The row opens the workspace; the\n * month sorts by its key, which orders chronologically.\n */\nconst COLUMNS: GridColDef<StaffUserAiUsageRow>[] = [\n {\n field: 'orgName',\n headerName: 'Organization',\n flex: 1,\n minWidth: 200,\n valueGetter: (_value, row) => row.orgName ?? row.orgId,\n },\n {\n field: 'month',\n headerName: 'Month',\n width: 140,\n valueFormatter: (value: string) => aiUsageMonthLabel(value),\n },\n {\n field: 'credits',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n {\n field: 'requests',\n headerName: 'Requests',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n {\n field: 'refusals',\n headerName: 'Refusals',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n listActionsColumn((row: StaffUserAiUsageRow) => (\n <ListRowActions\n label={row.orgName ?? row.orgId}\n quick={{ icon: mdiOpenInNew.path, label: 'View organization', to: orgHref(row.orgId), newTab: true }}\n items={[]}\n />\n )),\n]\n\n/**\n * ONE ACCOUNT'S AI USAGE ACROSS ORGANIZATIONS (AGL-2928), on the staff user\n * page: workspace, month, credits — every month kept, newest first.\n *\n * Read on mount, and it mounts only for a confirmed staff reader: the\n * `staffUser` zone sits inside the page's `StaffOnly`, which renders nothing\n * while the claim is still loading. The route records an access row about\n * this person on every open, so an open the page did not mean to make would\n * be an access nobody made.\n *\n * Above the table, the account's own Free allowance this month — what every\n * free workspace it owns draws on — and in the header the two acts that give\n * credits back to it (AGL-3595).\n */\nconst StaffUserAiUsageCard = ({ uid }: { uid: string }) => {\n const { data: user } = useUser()\n // Keyed on who is signed in, not on the user object's identity (AGL-2928).\n const signedInUid = user?.uid ?? null\n const userRef = useRef(user)\n userRef.current = user\n const [rows, setRows] = useState<StaffUserAiUsageRow[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [ready, setReady] = useState(false)\n const [allowance, setAllowance] = useState<StaffAiAccountAllowance | null>(null)\n // Bumped after a give-back, so the card re-reads the allowance it changed.\n const [version, setVersion] = useState(0)\n const [notice, setNotice] = useState<string | null>(null)\n const [returning, setReturning] = useState<'give' | 'reset' | null>(null)\n const router = useRouter()\n\n useEffect(() => {\n const user = userRef.current\n if (!uid || !signedInUid || !user) return undefined\n let active = true\n setReady(false)\n setError(null)\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/admin/user?uid=${encodeURIComponent(uid)}`,\n )\n const payload = await response.json().catch(() => null)\n if (!active) return\n if (!response.ok) {\n setError(payload?.error ?? 'AI usage lookup failed')\n setRows(null)\n } else {\n setRows((payload?.rows ?? []) as StaffUserAiUsageRow[])\n // Absent from a route older than give-backs.\n setAllowance((payload?.allowance ?? null) as StaffAiAccountAllowance | null)\n }\n } catch {\n if (active) {\n setError('AI usage lookup failed')\n setRows(null)\n }\n } finally {\n if (active) setReady(true)\n }\n })()\n return () => {\n active = false\n }\n }, [uid, signedInUid, version])\n\n return (\n <CardDisplay\n header={`${aiAddonName()} usage across organizations`}\n help={pluginDocsHelp('aiMonitoring', {\n anchor: '#one-account',\n excerpt:\n 'This account’s AI credits in every workspace it belongs to, month by month. Opening it is recorded as a staff access about this person.',\n })}\n HeaderProps={{\n action: (\n <StaffCreditReturnActions\n disabled={!allowance}\n onGive={() => setReturning('give')}\n onReset={() => setReturning('reset')}\n />\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {allowance ? (\n <StaffCreditReturnDialog\n open={returning !== null}\n mode={returning ?? 'give'}\n target={{ uid }}\n workspace={null}\n account={{ used: allowance.usedCredits, limit: allowance.limitCredits }}\n onClose={() => setReturning(null)}\n onReturned={(message) => {\n setReturning(null)\n setNotice(message)\n setVersion((current) => current + 1)\n }}\n />\n ) : null}\n {notice ? (\n <Alert severity=\"success\" onClose={() => setNotice(null)} sx={{ mb: 2 }}>\n {notice}\n </Alert>\n ) : null}\n {allowance ? (\n <Typography variant=\"body2\" sx={{ mb: 1 }}>\n {allowanceSentence(allowance, 'user')}\n </Typography>\n ) : null}\n {!ready ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : error || !rows ? (\n <Alert severity=\"warning\">\n {`Could not read this account’s AI usage — ${error ?? 'a failed read'}, not zero usage.`}\n </Alert>\n ) : rows.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI usage attributed to this account in any workspace it belongs to.'}\n </Typography>\n ) : (\n // Every month kept is in hand — at most fifty workspaces times the\n // retention window — so the grid pages and sorts the one read, and\n // offers no filter or search: a bounded readout is read whole, not\n // narrowed over the rows it holds (AGL-3321).\n <ListTable\n rows={rows}\n columns={COLUMNS}\n getRowId={rowId}\n rowHeight={TABLE_ROW_HEIGHT}\n onOpen={(_id, row: StaffUserAiUsageRow) => router.push(orgHref(row.orgId))}\n disableColumnFilter\n quickFilter={false}\n />\n )}\n </CardDisplay>\n )\n}\nStaffUserAiUsageCard.displayName = 'StaffUserAiUsageCard'\n\nexport default StaffUserAiUsageCard\n"],"names":["aiAddonName","buildRoute","Route","pluginDocsHelp","mdiOpenInNew","CardDisplay","ListRowActions","ListTable","listActionsColumn","TABLE_ROW_HEIGHT","authorizedFetch","useUser","Alert","Typography","useRouter","useEffect","useRef","useState","aiUsageMonthLabel","allowanceSentence","StaffCreditReturnActions","StaffCreditReturnDialog","orgHref","orgId","ADMIN_ORG_DETAIL","rowId","row","month","COLUMNS","field","headerName","flex","minWidth","valueGetter","_value","orgName","width","valueFormatter","value","type","align","headerAlign","toLocaleString","label","quick","icon","path","to","newTab","items","StaffUserAiUsageCard","uid","data","user","signedInUid","userRef","current","rows","setRows","error","setError","ready","setReady","allowance","setAllowance","version","setVersion","notice","setNotice","returning","setReturning","router","undefined","active","response","encodeURIComponent","payload","json","catch","ok","header","help","anchor","excerpt","HeaderProps","action","disabled","onGive","onReset","contentGutterX","contentGutterY","open","mode","target","workspace","account","used","usedCredits","limit","limitCredits","onClose","onReturned","message","severity","sx","mb","variant","color","length","columns","getRowId","rowHeight","onOpen","_id","push","disableColumnFilter","quickFilter","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,WAAW,QAAQ,eAAc;AAC1C,SAASC,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AACzE,SAASC,cAAc,QAAQ,mCAAkC;AACjE,SAASC,YAAY,QAAQ,yBAAwB;AACrD,SAASC,WAAW,QAAQ,uBAAsB;AAClD,SACEC,cAAc,EACdC,SAAS,EACTC,iBAAiB,QACZ,uDAAsD;AAC7D,SAASC,gBAAgB,QAAQ,8CAA6C;AAC9E,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,OAAO,QAAQ,iCAAgC;AACxD,SAASC,KAAK,EAAEC,UAAU,QAAQ,gBAAe;AAEjD,SAASC,SAAS,QAAQ,kBAAiB;AAC3C,SAASC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AACnD,SAASC,iBAAiB,QAAQ,4BAAwB;AAE1D,SAASC,iBAAiB,QAAQ,gCAA4B;AAC9D,SACEC,wBAAwB,EACxBC,uBAAuB,QAClB,wCAAoC;AAa3C,MAAMC,UAAU,CAACC,QAAkBtB,WAAWC,MAAMsB,gBAAgB,EAAE;QAAED;IAAM;AAE9E,2CAA2C,GAC3C,MAAME,QAAQ,CAACC,MAA6B,GAAGA,IAAIH,KAAK,CAAC,CAAC,EAAEG,IAAIC,KAAK,EAAE;AAEvE;;;CAGC,GACD,MAAMC,UAA6C;IACjD;QACEC,OAAO;QACPC,YAAY;QACZC,MAAM;QACNC,UAAU;QACVC,aAAa,CAACC,QAAQR;gBAAQA;oBAAAA,eAAAA,IAAIS,OAAO,YAAXT,eAAeA,IAAIH,KAAK;;IACxD;IACA;QACEM,OAAO;QACPC,YAAY;QACZM,OAAO;QACPC,gBAAgB,CAACC,QAAkBpB,kBAAkBoB;IACvD;IACA;QACET,OAAO;QACPC,YAAY;QACZS,MAAM;QACNC,OAAO;QACPC,aAAa;QACbL,OAAO;QACPC,gBAAgB,CAACC,QAAkBA,MAAMI,cAAc;IACzD;IACA;QACEb,OAAO;QACPC,YAAY;QACZS,MAAM;QACNC,OAAO;QACPC,aAAa;QACbL,OAAO;QACPC,gBAAgB,CAACC,QAAkBA,MAAMI,cAAc;IACzD;IACA;QACEb,OAAO;QACPC,YAAY;QACZS,MAAM;QACNC,OAAO;QACPC,aAAa;QACbL,OAAO;QACPC,gBAAgB,CAACC,QAAkBA,MAAMI,cAAc;IACzD;IACAlC,kBAAkB,CAACkB;YAERA;6BADT,KAACpB;YACCqC,KAAK,GAAEjB,eAAAA,IAAIS,OAAO,YAAXT,eAAeA,IAAIH,KAAK;YAC/BqB,OAAO;gBAAEC,MAAMzC,aAAa0C,IAAI;gBAAEH,OAAO;gBAAqBI,IAAIzB,QAAQI,IAAIH,KAAK;gBAAGyB,QAAQ;YAAK;YACnGC,OAAO,EAAE;;;CAGd;AAED;;;;;;;;;;;;;CAaC,GACD,MAAMC,uBAAuB,CAAC,EAAEC,GAAG,EAAmB;;IACpD,MAAM,EAAEC,MAAMC,IAAI,EAAE,GAAG1C;IACvB,2EAA2E;IAC3E,MAAM2C,sBAAcD,wBAAAA,KAAMF,GAAG,mBAAI;IACjC,MAAMI,UAAUvC,OAAOqC;IACvBE,QAAQC,OAAO,GAAGH;IAClB,MAAM,CAACI,MAAMC,QAAQ,GAAGzC,SAAuC;IAC/D,MAAM,CAAC0C,OAAOC,SAAS,GAAG3C,SAAwB;IAClD,MAAM,CAAC4C,OAAOC,SAAS,GAAG7C,SAAS;IACnC,MAAM,CAAC8C,WAAWC,aAAa,GAAG/C,SAAyC;IAC3E,2EAA2E;IAC3E,MAAM,CAACgD,SAASC,WAAW,GAAGjD,SAAS;IACvC,MAAM,CAACkD,QAAQC,UAAU,GAAGnD,SAAwB;IACpD,MAAM,CAACoD,WAAWC,aAAa,GAAGrD,SAAkC;IACpE,MAAMsD,SAASzD;IAEfC,UAAU;QACR,MAAMsC,OAAOE,QAAQC,OAAO;QAC5B,IAAI,CAACL,OAAO,CAACG,eAAe,CAACD,MAAM,OAAOmB;QAC1C,IAAIC,SAAS;QACbX,SAAS;QACTF,SAAS;QACT,KAAK,AAAC,CAAA;YACJ,IAAI;gBACF,MAAMc,WAAW,MAAMhE,gBACrB2C,MACA,CAAC,uBAAuB,EAAEsB,mBAAmBxB,MAAM;gBAErD,MAAMyB,UAAU,MAAMF,SAASG,IAAI,GAAGC,KAAK,CAAC,IAAM;gBAClD,IAAI,CAACL,QAAQ;gBACb,IAAI,CAACC,SAASK,EAAE,EAAE;;oBAChBnB,iBAASgB,2BAAAA,QAASjB,KAAK,mBAAI;oBAC3BD,QAAQ;gBACV,OAAO;;oBACLA,iBAASkB,2BAAAA,QAASnB,IAAI,oBAAI,EAAE;oBAC5B,6CAA6C;oBAC7CO,sBAAcY,2BAAAA,QAASb,SAAS,oBAAI;gBACtC;YACF,EAAE,eAAM;gBACN,IAAIU,QAAQ;oBACVb,SAAS;oBACTF,QAAQ;gBACV;YACF,SAAU;gBACR,IAAIe,QAAQX,SAAS;YACvB;QACF,CAAA;QACA,OAAO;YACLW,SAAS;QACX;IACF,GAAG;QAACtB;QAAKG;QAAaW;KAAQ;IAE9B,qBACE,MAAC5D;QACC2E,QAAQ,GAAGhF,cAAc,2BAA2B,CAAC;QACrDiF,MAAM9E,eAAe,gBAAgB;YACnC+E,QAAQ;YACRC,SACE;QACJ;QACAC,aAAa;YACXC,sBACE,KAACjE;gBACCkE,UAAU,CAACvB;gBACXwB,QAAQ,IAAMjB,aAAa;gBAC3BkB,SAAS,IAAMlB,aAAa;;QAGlC;QACAmB,cAAc;QACdC,cAAc;;YAEb3B,0BACC,KAAC1C;gBACCsE,MAAMtB,cAAc;gBACpBuB,IAAI,EAAEvB,oBAAAA,YAAa;gBACnBwB,QAAQ;oBAAE1C;gBAAI;gBACd2C,WAAW;gBACXC,SAAS;oBAAEC,MAAMjC,UAAUkC,WAAW;oBAAEC,OAAOnC,UAAUoC,YAAY;gBAAC;gBACtEC,SAAS,IAAM9B,aAAa;gBAC5B+B,YAAY,CAACC;oBACXhC,aAAa;oBACbF,UAAUkC;oBACVpC,WAAW,CAACV,UAAYA,UAAU;gBACpC;iBAEA;YACHW,uBACC,KAACvD;gBAAM2F,UAAS;gBAAUH,SAAS,IAAMhC,UAAU;gBAAOoC,IAAI;oBAAEC,IAAI;gBAAE;0BACnEtC;iBAED;YACHJ,0BACC,KAAClD;gBAAW6F,SAAQ;gBAAQF,IAAI;oBAAEC,IAAI;gBAAE;0BACrCtF,kBAAkB4C,WAAW;iBAE9B;YACH,CAACF,sBACA,KAAChD;gBAAW6F,SAAQ;gBAAQC,OAAM;0BAC/B;iBAEDhD,SAAS,CAACF,qBACZ,KAAC7C;gBAAM2F,UAAS;0BACb,CAAC,yCAAyC,EAAE5C,gBAAAA,QAAS,gBAAgB,iBAAiB,CAAC;iBAExFF,KAAKmD,MAAM,KAAK,kBAClB,KAAC/F;gBAAW6F,SAAQ;gBAAQC,OAAM;0BAC/B;iBAGH,mEAAmE;YACnE,mEAAmE;YACnE,mEAAmE;YACnE,8CAA8C;0BAC9C,KAACpG;gBACCkD,MAAMA;gBACNoD,SAASjF;gBACTkF,UAAUrF;gBACVsF,WAAWtG;gBACXuG,QAAQ,CAACC,KAAKvF,MAA6B6C,OAAO2C,IAAI,CAAC5F,QAAQI,IAAIH,KAAK;gBACxE4F,mBAAmB;gBACnBC,aAAa;;;;AAKvB;AACAlE,qBAAqBmE,WAAW,GAAG;AAEnC,eAAenE,qBAAoB"}
@@ -104,7 +104,20 @@ export declare const AI_JOB_PAGE_LANGUAGE_BUDGET: import("./ai-job-budget").AiJo
104
104
  export declare const AI_LAYOUT_PAGE_KIND = "layout-page";
105
105
  /** The language, as the page door's cached instructions. */
106
106
  export declare const AI_JOB_PAGE_LANGUAGE_INSTRUCTIONS: readonly AiSystemBlock[];
107
- /** The page of a site plan that places its form: the form the plan creates, or one the site has. */
107
+ /**
108
+ * The page of a site plan that places its form: the form the plan creates, or
109
+ * one the site has. Home only when no other page places it (AGL-3805).
110
+ *
111
+ * Every call to action that asks a visitor to get in touch goes here — the
112
+ * header's button on every page, the footer's, a page's own — so a page whose
113
+ * purpose is the form outranks the home page that closes on a copy of it. A
114
+ * plan lists Home first, and a home page that ends "visit us or order" places
115
+ * the form too: the first page placing it was Home, and a Spanish bakery's
116
+ * header "Pide tu pastel" and footer "Contáctanos" (beta.253, job eg4maZMGtN)
117
+ * linked the home page on every page, itself included, while /pasteles and
118
+ * /visitanos held the form. Eight of the 21 newest AI-built sites linked their
119
+ * header's call to action home the same way, six of them with such a page.
120
+ */
108
121
  export declare function aiLayoutFormPageOfPlan(plan: Pick<AiJobPlan, 'create' | 'screens'>, formIds?: readonly string[]): string | null;
109
122
  /** Every word the job was given, which a page's contact details are held to. */
110
123
  export declare function aiLayoutFacts(job: Pick<AiJob, 'brief' | 'inputs'>): string;