@agent-native/core 0.145.7 → 0.145.8

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 (92) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +5 -1
  3. package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +3 -2
  4. package/corpus/templates/analytics/changelog/2026-08-07-settings-skeleton-placeholders.md +6 -0
  5. package/corpus/templates/analytics/netlify.toml +1 -1
  6. package/corpus/templates/analytics/package.json +1 -0
  7. package/corpus/templates/analytics/scripts/migrate-production.ts +24 -0
  8. package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +9 -0
  9. package/corpus/templates/analytics/server/plugins/db.ts +14 -3
  10. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +5 -1
  11. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +5 -1
  12. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +5 -1
  13. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -1
  14. package/corpus/templates/chat/AGENTS.md +3 -0
  15. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +5 -1
  16. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +5 -1
  17. package/corpus/templates/crm/app/components/crm/IntelligenceSettings.tsx +21 -3
  18. package/corpus/templates/crm/app/components/crm/settings/ConnectionSettings.tsx +23 -5
  19. package/corpus/templates/crm/app/components/crm/settings/FieldsSettings.tsx +24 -5
  20. package/corpus/templates/crm/app/components/crm/settings/ListsSettings.tsx +23 -3
  21. package/corpus/templates/crm/changelog/2026-08-07-settings-loading-skeletons.md +6 -0
  22. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +5 -1
  23. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +5 -1
  24. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +5 -1
  25. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +5 -1
  26. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +5 -1
  27. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +5 -1
  28. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +5 -1
  29. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +5 -1
  30. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +5 -1
  31. package/dist/client/components/ApiKeySettings.js +2 -5
  32. package/dist/client/org/TeamPage.js +4 -2
  33. package/dist/client/settings/AccountSettingsCard.js +2 -1
  34. package/dist/client/settings/AgentsSection.js +2 -1
  35. package/dist/client/settings/SecretsSection.js +3 -2
  36. package/dist/client/settings/SettingsPanel.js +6 -12
  37. package/dist/client/settings/SettingsSkeleton.d.ts +12 -0
  38. package/dist/client/settings/SettingsSkeleton.js +12 -0
  39. package/dist/client/settings/VoiceTranscriptionSection.js +5 -4
  40. package/dist/client/settings/index.d.ts +1 -0
  41. package/dist/client/settings/index.js +1 -0
  42. package/dist/collab/struct-routes.d.ts +1 -1
  43. package/dist/db/client.d.ts +13 -7
  44. package/dist/db/client.js +124 -7
  45. package/dist/db/ddl-guard.js +3 -1
  46. package/dist/db/ensure-additive-columns.d.ts +2 -0
  47. package/dist/db/ensure-additive-columns.js +5 -2
  48. package/dist/db/index.d.ts +2 -2
  49. package/dist/db/index.js +2 -2
  50. package/dist/db/migrations.d.ts +11 -54
  51. package/dist/db/migrations.js +70 -0
  52. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  53. package/dist/jobs/index.d.ts +1 -0
  54. package/dist/jobs/index.js +1 -0
  55. package/dist/jobs/run-history.d.ts +4 -0
  56. package/dist/jobs/run-history.js +44 -0
  57. package/dist/notifications/routes.d.ts +2 -2
  58. package/dist/observability/routes.d.ts +1 -1
  59. package/dist/progress/routes.d.ts +1 -1
  60. package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
  61. package/dist/provider-api/actions/provider-api.d.ts +12 -12
  62. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  63. package/dist/resources/handlers.d.ts +1 -1
  64. package/dist/secrets/routes.d.ts +6 -6
  65. package/dist/server/agent-chat-plugin.js +6 -4
  66. package/dist/server/auth-plugin.js +5 -0
  67. package/dist/server/better-auth-instance.js +0 -61
  68. package/dist/server/better-auth-migrations.d.ts +8 -0
  69. package/dist/server/better-auth-migrations.js +179 -0
  70. package/dist/server/edge.d.ts +2 -0
  71. package/dist/server/edge.js +2 -0
  72. package/dist/server/index.d.ts +2 -0
  73. package/dist/server/index.js +2 -0
  74. package/dist/server/release-migrations.d.ts +8 -0
  75. package/dist/server/release-migrations.js +24 -0
  76. package/dist/server/sweep-runtime.d.ts +1 -1
  77. package/dist/server/sweep-runtime.js +15 -0
  78. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -1
  79. package/dist/templates/chat/AGENTS.md +3 -0
  80. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +5 -1
  81. package/dist/templates/default/AGENTS.md +3 -0
  82. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -1
  83. package/dist/templates/workspace-core/AGENTS.md +3 -0
  84. package/dist/templates/workspace-root/AGENTS.md +3 -0
  85. package/package.json +1 -1
  86. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +5 -1
  87. package/src/templates/chat/AGENTS.md +3 -0
  88. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +5 -1
  89. package/src/templates/default/AGENTS.md +3 -0
  90. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +5 -1
  91. package/src/templates/workspace-core/AGENTS.md +3 -0
  92. package/src/templates/workspace-root/AGENTS.md +3 -0
package/corpus/README.md CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 7888
34
+ - template files: 7891
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -58,6 +58,7 @@ import {
58
58
  SelectTrigger,
59
59
  SelectValue,
60
60
  } from "@/components/ui/select";
61
+ import { Skeleton } from "@/components/ui/skeleton";
61
62
  import { Switch } from "@/components/ui/switch";
62
63
  import { Textarea } from "@/components/ui/textarea";
63
64
  import { cn } from "@/lib/utils";
@@ -503,9 +504,9 @@ export function AlertRulesSettingsCard() {
503
504
  {isLoading ? (
504
505
  <div className="space-y-2">
505
506
  {[0, 1, 2].map((item) => (
506
- <div
507
+ <Skeleton
507
508
  key={item}
508
- className="h-14 rounded-md border border-border bg-muted/30"
509
+ className="h-14 w-full rounded-md border border-border bg-muted/30"
509
510
  />
510
511
  ))}
511
512
  </div>
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-07
4
+ ---
5
+
6
+ Settings show stable skeleton placeholders while alert rules load.
@@ -4,7 +4,7 @@ ignore = "node ./scripts/netlify-ignore-build.mjs analytics"
4
4
  # core explicitly here so a cached Netlify install cannot bundle an older agent
5
5
  # loop into Analytics' server functions (including A2A/MCP final-response
6
6
  # guards).
7
- command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && pnpm --filter @agent-native/core build && NITRO_PRESET=netlify pnpm --filter analytics build"
7
+ command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && pnpm --filter @agent-native/core build && NITRO_PRESET=netlify pnpm --filter analytics build && if [ \"${CONTEXT:-}\" = \"production\" ]; then pnpm --filter analytics migrate:production; fi"
8
8
  publish = "templates/analytics/dist"
9
9
  functions = "templates/analytics/.netlify/functions-internal"
10
10
 
@@ -10,6 +10,7 @@
10
10
  "test": "vitest --run",
11
11
  "format.fix": "oxfmt --write .",
12
12
  "typecheck": "agent-native typecheck",
13
+ "migrate:production": "tsx scripts/migrate-production.ts",
13
14
  "action": "agent-native action",
14
15
  "script": "agent-native script"
15
16
  },
@@ -0,0 +1,24 @@
1
+ import { closeDbExec, withMigrationRuntime } from "@agent-native/core/db";
2
+ import { runFrameworkReleaseMigrations } from "@agent-native/core/server";
3
+
4
+ import { runAnalyticsMigrations } from "../server/plugins/db.js";
5
+
6
+ /**
7
+ * Release-time schema entrypoint for Analytics.
8
+ *
9
+ * This script is the production owner of schema changes. It runs against the
10
+ * direct migration endpoint selected by core, while request functions skip
11
+ * all migration and ensure-table work automatically.
12
+ */
13
+ async function main(): Promise<void> {
14
+ await withMigrationRuntime(async () => {
15
+ await runFrameworkReleaseMigrations(null);
16
+ await runAnalyticsMigrations(null);
17
+ });
18
+ }
19
+
20
+ try {
21
+ await main();
22
+ } finally {
23
+ await closeDbExec();
24
+ }
@@ -243,6 +243,15 @@ async function runTransactionalBackfill(
243
243
 
244
244
  return db.transaction(async (tx) => {
245
245
  if (isPostgres()) {
246
+ // This scans all of analytics_events (41 GB in production) in one
247
+ // GROUP BY, and the lease budgets 15 minutes for it. A role-level
248
+ // statement_timeout — the thing that stops a runaway dashboard query
249
+ // from holding every pooler connection — would otherwise kill this
250
+ // legitimate job partway through. SET LOCAL is scoped to this
251
+ // transaction and reverts on commit or rollback.
252
+ await tx.execute(
253
+ `SET LOCAL statement_timeout = '${BACKFILL_LEASE_MINUTES}min'`,
254
+ );
246
255
  const lockResult = await tx.execute({
247
256
  sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0::bigint)) AS acquired",
248
257
  args: [FIRST_PARTY_ANALYTICS_ROLLUP_LOCK_KEY],
@@ -2,6 +2,7 @@ import {
2
2
  ensureAdditiveColumns,
3
3
  getDbExec,
4
4
  runMigrations,
5
+ withMigrationRuntime,
5
6
  } from "@agent-native/core/db";
6
7
  import { isInBackgroundFunctionRuntime } from "@agent-native/core/server";
7
8
 
@@ -33,7 +34,7 @@ const schemaTables = Object.values(schema).filter(isDrizzleTable);
33
34
  // packages/core/src/db/migrations.ts for the full rationale). Version numbers
34
35
  // alone are not a safe identity across parallel branches that each extend
35
36
  // this list independently — see the v75-v83 incident documented on v75 below.
36
- const runAnalyticsMigrations = runMigrations(
37
+ export const runAnalyticsMigrations = runMigrations(
37
38
  [
38
39
  {
39
40
  version: 1,
@@ -1536,9 +1537,19 @@ export default async (nitroApp: any): Promise<void> => {
1536
1537
  }
1537
1538
  // The schema must exist before the first query. Measured cost on this
1538
1539
  // database (180 tables): ~5.5s for the version check alone, which is why the
1539
- // serverless runtime never runs it on cold starts.
1540
+ // serverless runtime never runs it on cold starts. The scheduled worker is
1541
+ // the one serverless exception and claims migration duty explicitly.
1540
1542
  // guard:allow-boot-data-work — schema must exist before the first query
1541
- await runAnalyticsMigrations(nitroApp);
1543
+ if (isScheduledRollupRuntime) {
1544
+ // guard:allow-boot-data-work — scheduled worker owns the release migration
1545
+ await withMigrationRuntime(async () => {
1546
+ // guard:allow-boot-data-work — scheduled worker owns the release migration
1547
+ await runAnalyticsMigrations(nitroApp);
1548
+ });
1549
+ } else {
1550
+ // guard:allow-boot-data-work — long-lived local runtime owns the migration
1551
+ await runAnalyticsMigrations(nitroApp);
1552
+ }
1542
1553
  try {
1543
1554
  const summary = await ensureAdditiveColumns({
1544
1555
  db: getDbExec(),
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -23,6 +23,9 @@ workflow needs durable UI around the conversation.
23
23
  - Keep the first viewport sparse and task-focused. Use progressive disclosure
24
24
  and domain-specific navigation, and never use sparkle, wand, magic, or robot
25
25
  icons as AI affordances.
26
+ - Page and section data loads use layout-matching `Skeleton` geometry, never a
27
+ generic "Loading..." label. Reserve `Spinner` for brief mutations, uploads,
28
+ and progress actions.
26
29
  - Use a sans-first SaaS hierarchy with one restrained visual cue; reserve serif
27
30
  type for content previews. Give the AgentSidebar a subtle surface/divider
28
31
  boundary, and stack original/generated review vertically by default.
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -39,6 +39,7 @@ import {
39
39
  SelectTrigger,
40
40
  SelectValue,
41
41
  } from "@/components/ui/select";
42
+ import { Skeleton } from "@/components/ui/skeleton";
42
43
  import { Switch } from "@/components/ui/switch";
43
44
  import { Textarea } from "@/components/ui/textarea";
44
45
 
@@ -131,9 +132,26 @@ export function IntelligenceSettings() {
131
132
  </div>
132
133
 
133
134
  {trackersQuery.isLoading ? (
134
- <p className="mt-8 text-sm text-muted-foreground">
135
- {t("intelligence.loading")}
136
- </p>
135
+ <div
136
+ className="mt-8 space-y-2"
137
+ role="status"
138
+ aria-busy="true"
139
+ aria-label={t("intelligence.loading")}
140
+ >
141
+ {[0, 1, 2].map((item) => (
142
+ <div
143
+ key={item}
144
+ className="flex items-center gap-4 rounded-lg border border-border/70 bg-card px-4 py-3.5"
145
+ >
146
+ <Skeleton className="size-4" />
147
+ <div className="flex-1 space-y-2">
148
+ <Skeleton className="h-4 w-40" />
149
+ <Skeleton className="h-3 w-56" />
150
+ </div>
151
+ <Skeleton className="size-5 rounded-full" />
152
+ </div>
153
+ ))}
154
+ </div>
137
155
  ) : trackers.length ? (
138
156
  <div className="mt-8 divide-y divide-border/70 rounded-lg border border-border/70 bg-card">
139
157
  {trackers.map((tracker) => {
@@ -5,6 +5,7 @@ import { Link } from "react-router";
5
5
 
6
6
  import { Badge } from "@/components/ui/badge";
7
7
  import { Button } from "@/components/ui/button";
8
+ import { Skeleton } from "@/components/ui/skeleton";
8
9
 
9
10
  import {
10
11
  connectionModeInfo,
@@ -88,13 +89,30 @@ export function ConnectionSettings() {
88
89
  <ConnectionRow key={connection.id} connection={connection} />
89
90
  ))}
90
91
  </div>
92
+ ) : connectionsQuery.isLoading ? (
93
+ <div
94
+ className="mt-6 space-y-2"
95
+ role="status"
96
+ aria-busy="true"
97
+ aria-label={t("connection.loading")}
98
+ >
99
+ {[0, 1, 2].map((item) => (
100
+ <div
101
+ key={item}
102
+ className="flex items-center gap-4 rounded-lg border border-border/70 bg-card px-4 py-3.5"
103
+ >
104
+ <Skeleton className="size-4" />
105
+ <div className="flex-1 space-y-2">
106
+ <Skeleton className="h-4 w-40" />
107
+ <Skeleton className="h-3 w-56" />
108
+ </div>
109
+ <Skeleton className="h-6 w-20 rounded-full" />
110
+ </div>
111
+ ))}
112
+ </div>
91
113
  ) : (
92
114
  <div className="mt-6 rounded-lg border border-dashed border-border px-4 py-10 text-center">
93
- <p className="text-sm font-medium">
94
- {connectionsQuery.isLoading
95
- ? t("connection.loading")
96
- : t("connection.emptyTitle")}
97
- </p>
115
+ <p className="text-sm font-medium">{t("connection.emptyTitle")}</p>
98
116
  <p className="mt-1 text-sm text-muted-foreground">
99
117
  {t("connection.emptyDescription")}
100
118
  </p>
@@ -49,6 +49,7 @@ import {
49
49
  SelectTrigger,
50
50
  SelectValue,
51
51
  } from "@/components/ui/select";
52
+ import { Skeleton } from "@/components/ui/skeleton";
52
53
  import { Switch } from "@/components/ui/switch";
53
54
  import {
54
55
  Table,
@@ -311,13 +312,31 @@ export function FieldsSettings() {
311
312
  </TableBody>
312
313
  </Table>
313
314
  </div>
315
+ ) : activeTarget &&
316
+ (connectionsQuery.isLoading ||
317
+ listsQuery.isLoading ||
318
+ attributesQuery.isLoading) ? (
319
+ <div
320
+ className="mt-4 space-y-2"
321
+ role="status"
322
+ aria-busy="true"
323
+ aria-label={t("fields.loading")}
324
+ >
325
+ {[0, 1, 2, 3].map((item) => (
326
+ <div
327
+ key={item}
328
+ className="grid grid-cols-4 gap-4 rounded-lg border border-border/70 bg-card px-4 py-3.5"
329
+ >
330
+ <Skeleton className="h-4 w-36" />
331
+ <Skeleton className="h-4 w-28" />
332
+ <Skeleton className="h-4 w-24" />
333
+ <Skeleton className="h-4 w-32" />
334
+ </div>
335
+ ))}
336
+ </div>
314
337
  ) : (
315
338
  <div className="mt-4 rounded-lg border border-dashed border-border px-4 py-10 text-center">
316
- <p className="text-sm font-medium">
317
- {attributesQuery.isLoading
318
- ? t("fields.loading")
319
- : t("fields.emptyTitle")}
320
- </p>
339
+ <p className="text-sm font-medium">{t("fields.emptyTitle")}</p>
321
340
  <p className="mt-1 text-sm text-muted-foreground">
322
341
  {t("fields.emptyDescription")}
323
342
  </p>
@@ -45,6 +45,7 @@ import {
45
45
  SelectTrigger,
46
46
  SelectValue,
47
47
  } from "@/components/ui/select";
48
+ import { Skeleton } from "@/components/ui/skeleton";
48
49
  import { Textarea } from "@/components/ui/textarea";
49
50
 
50
51
  interface CrmListRow {
@@ -195,11 +196,30 @@ export function ListsSettings() {
195
196
  </section>
196
197
  ))}
197
198
  </div>
199
+ ) : listsQuery.isLoading ? (
200
+ <div
201
+ className="mt-6 space-y-2"
202
+ role="status"
203
+ aria-busy="true"
204
+ aria-label={t("lists.loading")}
205
+ >
206
+ {[0, 1, 2].map((item) => (
207
+ <div
208
+ key={item}
209
+ className="flex items-center gap-4 rounded-lg border border-border/70 bg-card px-4 py-3.5"
210
+ >
211
+ <Skeleton className="size-4" />
212
+ <div className="flex-1 space-y-2">
213
+ <Skeleton className="h-4 w-40" />
214
+ <Skeleton className="h-3 w-56" />
215
+ </div>
216
+ <Skeleton className="size-8 rounded-md" />
217
+ </div>
218
+ ))}
219
+ </div>
198
220
  ) : (
199
221
  <div className="mt-6 rounded-lg border border-dashed border-border px-4 py-10 text-center">
200
- <p className="text-sm font-medium">
201
- {listsQuery.isLoading ? t("lists.loading") : t("lists.emptyTitle")}
202
- </p>
222
+ <p className="text-sm font-medium">{t("lists.emptyTitle")}</p>
203
223
  <p className="mt-1 text-sm text-muted-foreground">
204
224
  {t("lists.emptyDescription")}
205
225
  </p>
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-07
4
+ ---
5
+
6
+ CRM settings show stable skeleton placeholders while lists, fields, connections, and intelligence trackers load.
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -199,7 +199,11 @@ as an operator would use it repeatedly:
199
199
  - Use full `Card` composition when the content has a title, description, content, or actions. Do not dump complex cards into a single `CardContent`.
200
200
  - Use `ToggleGroup` for small option sets, `Switch` for binary settings, `Checkbox` for multi-select, `RadioGroup` for one-of-many, and `Slider`/inputs for numeric values.
201
201
  - For forms, prefer the app's existing shadcn form pattern. If newer `Field`, `FieldGroup`, or `InputGroup` primitives are installed or appropriate to add, use them instead of raw layout divs.
202
- - Loading states use `Skeleton`, `Progress`, `Spinner`, or the app's existing loading primitives. Empty states should have one clear next action.
202
+ - Page and section data loading uses layout-matching `Skeleton` geometry. Do
203
+ not show generic "Loading..." text for content loads; reserve `Spinner` for
204
+ brief mutations, uploads, and progress actions. Use the app's existing
205
+ loading primitive when it is a genuine design-system adapter. Empty states
206
+ should have one clear next action.
203
207
 
204
208
  ## Anti-Patterns
205
209
 
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Skeleton } from "@agent-native/toolkit/design-system";
2
3
  import { useState, useEffect, useCallback } from "react";
3
4
  import { agentNativePath } from "../api-path.js";
4
5
  /**
@@ -77,7 +78,7 @@ export function ApiKeySettings({ settingsPath: _settingsPath = "/settings", }) {
77
78
  };
78
79
  const pendingCount = Object.values(values).filter((v) => v.trim() !== "").length;
79
80
  if (loading) {
80
- return (_jsx("div", { style: styles.container, children: _jsx("p", { style: styles.loadingText, children: "Loading API key status..." }) }));
81
+ return (_jsxs("div", { style: styles.container, role: "status", "aria-busy": "true", "aria-label": "Loading API key status", children: [_jsx(Skeleton, { className: "h-3 w-40" }), _jsx(Skeleton, { className: "mt-3 h-8 w-full" })] }));
81
82
  }
82
83
  if (error) {
83
84
  return (_jsxs("div", { style: styles.container, children: [_jsx("p", { style: styles.errorText, children: error }), _jsx("button", { onClick: fetchStatus, style: styles.retryButton, children: "Retry" })] }));
@@ -110,10 +111,6 @@ const styles = {
110
111
  opacity: 0.6,
111
112
  margin: "0 0 16px 0",
112
113
  },
113
- loadingText: {
114
- fontSize: "13px",
115
- opacity: 0.5,
116
- },
117
114
  errorText: {
118
115
  fontSize: "13px",
119
116
  color: "#ef4444",