@agent-native/core 0.146.0 → 0.146.2

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 (120) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +4 -0
  3. package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  4. package/corpus/templates/analytics/actions/compose-dashboard.ts +2 -27
  5. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +7 -8
  6. package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +18 -2
  7. package/corpus/templates/analytics/actions/mutate-dashboard.ts +16 -24
  8. package/corpus/templates/analytics/actions/rename-dashboard.ts +2 -24
  9. package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +2 -24
  10. package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +2 -25
  11. package/corpus/templates/analytics/actions/update-dashboard.ts +4 -32
  12. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +1 -40
  13. package/corpus/templates/analytics/changelog/2026-08-07-language-selection-now-lives-in-settings-instead-of-the-comm.md +6 -0
  14. package/corpus/templates/analytics/netlify.toml +7 -0
  15. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +13 -13
  16. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +4 -2
  17. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +0 -35
  18. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +5 -3
  19. package/corpus/templates/analytics/server/lib/dashboard-collab-sync.ts +77 -0
  20. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +2 -23
  21. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +28 -74
  22. package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +5 -9
  23. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +4 -0
  24. package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  25. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +4 -0
  26. package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  27. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +4 -0
  28. package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  29. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +4 -0
  30. package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  31. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +4 -0
  32. package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +1 -1
  34. package/corpus/templates/clips/changelog/2026-08-07-fixed-trimmed-clips-durations-so-the-main-player-display-mat.md +6 -0
  35. package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +1 -1
  36. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  37. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +4 -0
  38. package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  39. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +4 -0
  40. package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  41. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +4 -0
  42. package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  43. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +4 -0
  44. package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  45. package/corpus/templates/dispatch/changelog/2026-08-07-language-selection-now-lives-in-settings-instead-of-the-disp.md +6 -0
  46. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +4 -0
  47. package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  48. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +59 -22
  49. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +4 -0
  50. package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  51. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +4 -0
  52. package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  53. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +4 -0
  54. package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  55. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +4 -0
  56. package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  57. package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +10 -1
  58. package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +10 -3
  59. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +4 -0
  60. package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  61. package/corpus/templates/slides/actions/generate-image-api.ts +123 -0
  62. package/corpus/templates/slides/actions/import-file.ts +144 -99
  63. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +10 -2
  64. package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +1 -1
  65. package/corpus/templates/slides/changelog/2026-08-07-deleting-a-deck-no-longer-freezes-the-slides-browser.md +6 -0
  66. package/corpus/templates/slides/changelog/2026-08-07-slides-verify-generated-images-are-persisted-on-the-target-s.md +6 -0
  67. package/corpus/templates/slides/package.json +1 -0
  68. package/corpus/templates/slides/server/handlers/import/html-converter.ts +9 -0
  69. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +960 -0
  70. package/corpus/templates/slides/server/lib/slide-image-insertion.ts +141 -0
  71. package/corpus/templates/slides/server/plugins/agent-chat.ts +2 -0
  72. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +4 -0
  73. package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  74. package/dist/client/AgentPanel.d.ts +10 -4
  75. package/dist/client/AgentPanel.js +29 -82
  76. package/dist/collab/awareness.d.ts +2 -2
  77. package/dist/collab/struct-routes.d.ts +1 -1
  78. package/dist/db/migrations.js +22 -4
  79. package/dist/deploy/build.d.ts +1 -0
  80. package/dist/deploy/build.js +27 -22
  81. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  82. package/dist/ingestion/pptx.d.ts +1 -0
  83. package/dist/ingestion/pptx.js +353 -28
  84. package/dist/notifications/routes.d.ts +2 -2
  85. package/dist/observability/routes.d.ts +1 -1
  86. package/dist/progress/routes.d.ts +1 -1
  87. package/dist/resources/handlers.d.ts +1 -1
  88. package/dist/secrets/routes.d.ts +6 -6
  89. package/dist/server/builder-browser.d.ts +34 -0
  90. package/dist/server/builder-browser.js +179 -0
  91. package/dist/server/core-routes-plugin.d.ts +5 -0
  92. package/dist/server/core-routes-plugin.js +24 -8
  93. package/dist/server/index.d.ts +1 -1
  94. package/dist/server/index.js +1 -1
  95. package/dist/shared/cache-control.d.ts +2 -2
  96. package/dist/shared/cache-control.js +2 -2
  97. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +4 -0
  98. package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  99. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +4 -0
  100. package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  101. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +4 -0
  102. package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  103. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +4 -0
  104. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  105. package/dist/vite/client.js +13 -1
  106. package/docs/content/drop-in-agent.mdx +3 -3
  107. package/docs/content/getting-started-actions.mdx +1 -2
  108. package/docs/content/getting-started-pages.mdx +1 -2
  109. package/docs/content/getting-started.mdx +0 -4
  110. package/docs/content/what-is-agent-native.mdx +4 -4
  111. package/package.json +3 -3
  112. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +4 -0
  113. package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  114. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +4 -0
  115. package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  116. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +4 -0
  117. package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  118. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +4 -0
  119. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  120. package/docs/content/getting-started-database.mdx +0 -173
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: 7896
34
+ - template files: 7904
@@ -27,6 +27,10 @@ The build location depends on the host. Treat this as a hard routing rule:
27
27
  `resourceIds` when available. This is the Builder handoff.
28
28
  - Do not substitute the generic `create_workspace_app` MCP tool in an online
29
29
  host. That tool is a local workspace scaffolder, not the Builder handoff.
30
+ - Connect the Agent-Native Dispatch MCP connector only. Dispatch uses the
31
+ authenticated Builder Projects API to reuse or provision the workspace
32
+ project before starting the Builder Cloud Agent; a separate Builder CMS MCP
33
+ connection is not required for this workflow.
30
34
  - If `start-workspace-app-creation` is not available or Dispatch is not
31
35
  authenticated, stop with the connector setup needed. Do not fall back to a
32
36
  local build or claim that the app exists.
@@ -50,6 +50,14 @@ skills. For a ChatGPT Project or any host that exposes only the MCP connector,
50
50
  also place the skill instructions or this reference in the Project
51
51
  instructions/knowledge files. Then add and authenticate the Dispatch MCP
52
52
  connector from the packaged `adapters/chatgpt-mcp/connector.json`.
53
+ This single connector is enough: Dispatch provisions or reuses the Builder
54
+ project through the Builder Projects API, so do not ask the user to add a
55
+ separate Builder CMS MCP for app creation.
56
+ Builder's separate Fusion MCP is available as a custom remote connector at
57
+ `https://mcp.builder.io/mcp/fusion` for direct Builder work. It can run an
58
+ existing project, but it cannot provision the repo-backed Agent-Native
59
+ workspace project, so it is optional and not a replacement for the Dispatch
60
+ handoff in this skill. Do not add Builder's CMS MCP for this workflow.
53
61
 
54
62
  For Claude Web, ChatGPT Web, and their web Projects, the host is an
55
63
  orchestrator, not the build environment. After forming the source brief, call
@@ -1,9 +1,4 @@
1
1
  import { defineAction, embedApp } from "@agent-native/core";
2
- import {
3
- hasCollabState,
4
- applyText,
5
- seedFromText,
6
- } from "@agent-native/core/collab";
7
2
  import {
8
3
  getRequestUserEmail,
9
4
  getRequestOrgId,
@@ -11,6 +6,7 @@ import {
11
6
  } from "@agent-native/core/server";
12
7
  import { z } from "zod";
13
8
 
9
+ import { queueDashboardCollabSync } from "../server/lib/dashboard-collab-sync";
14
10
  import { validateFirstPartyDashboardTimeScope } from "../server/lib/dashboard-time-scope";
15
11
  import {
16
12
  getDashboard,
@@ -27,27 +23,6 @@ import {
27
23
  usesFirstPartyDashboardFilters,
28
24
  } from "../server/lib/first-party-metric-catalog";
29
25
 
30
- /**
31
- * Push the saved config through the collab layer so open dashboard editors get
32
- * the change in real time (mirrors update-dashboard).
33
- */
34
- async function syncToCollab(
35
- dashboardId: string,
36
- config: Record<string, unknown>,
37
- ): Promise<void> {
38
- const docId = `dash-${dashboardId}`;
39
- const configStr = JSON.stringify(config);
40
- try {
41
- if (await hasCollabState(docId)) {
42
- await applyText(docId, configStr, "content", "agent");
43
- } else {
44
- await seedFromText(docId, configStr);
45
- }
46
- } catch {
47
- // Collab sync is best-effort — the SQL write is the source of truth.
48
- }
49
- }
50
-
51
26
  const WINDOWS = new Set<MetricWindow>(["30d", "90d", "all"]);
52
27
 
53
28
  function normalizeWindow(raw: unknown): MetricWindow | undefined {
@@ -329,7 +304,7 @@ export default defineAction({
329
304
  ? (finalConfig.panels as unknown[]).length
330
305
  : 0;
331
306
 
332
- await syncToCollab(args.dashboardId, finalConfig);
307
+ queueDashboardCollabSync(args.dashboardId, finalConfig, "agent");
333
308
 
334
309
  const parts: string[] = [];
335
310
  if (existing && !args.overwrite) {
@@ -65,16 +65,15 @@ export default defineAction({
65
65
  const dash = await getDashboard(args.id, ctx);
66
66
  if (!dash || dash.kind !== "sql") {
67
67
  const seed = loadDashboardSeed(args.id);
68
- if (seed)
69
- return buildDashboardSeedAgentContext(
70
- args.id,
68
+ if (seed) {
69
+ const config =
71
70
  args.id === FIRST_PARTY_DASHBOARD_ID
72
71
  ? repairCanonicalFirstPartyDashboardQueries(seed).config
73
- : seed,
74
- {
75
- includeConfig: args.includeConfig === true,
76
- },
77
- );
72
+ : seed;
73
+ return buildDashboardSeedAgentContext(args.id, config, {
74
+ includeConfig: args.includeConfig === true,
75
+ });
76
+ }
78
77
  throw Object.assign(new Error("Dashboard not found"), {
79
78
  statusCode: 404,
80
79
  });
@@ -100,16 +100,32 @@ export default defineAction({
100
100
  `BigQuery backfill already targets ${existingJob.table}; prepare the existing migration before changing tables.`,
101
101
  );
102
102
  }
103
+ const initialCursor =
104
+ existingJob?.cursor ?? current.backfillCursor ?? null;
105
+ if (
106
+ !existingJob &&
107
+ current.sink === "dual" &&
108
+ !initialCursor &&
109
+ ready.rowCount > 0
110
+ ) {
111
+ throw new Error(
112
+ "Refusing to restart an existing dual-write BigQuery backfill without its legacy cursor; recover the migration cursor before preparing again.",
113
+ );
114
+ }
115
+ const initialCompleted =
116
+ existingJob?.status === "completed" ||
117
+ current.backfillCompleted === true;
103
118
  await saveFirstPartyAnalyticsBackend(scope, {
104
119
  sink: "dual",
105
120
  table: ready.table.fullyQualified,
106
- backfillCursor: existingJob?.cursor ?? null,
107
- backfillCompleted: existingJob?.status === "completed",
121
+ backfillCursor: initialCursor,
122
+ backfillCompleted: initialCompleted,
108
123
  });
109
124
  const job = await queueFirstPartyAnalyticsBigQueryBackfill(
110
125
  scope,
111
126
  ready.table.fullyQualified,
112
127
  limit,
128
+ initialCursor,
113
129
  );
114
130
  return {
115
131
  sink: "dual" as const,
@@ -1,9 +1,4 @@
1
1
  import { defineAction, embedApp } from "@agent-native/core";
2
- import {
3
- applyText,
4
- hasCollabState,
5
- seedFromText,
6
- } from "@agent-native/core/collab";
7
2
  import {
8
3
  buildDeepLink,
9
4
  getRequestOrgId,
@@ -11,6 +6,10 @@ import {
11
6
  } from "@agent-native/core/server";
12
7
  import { z } from "zod";
13
8
 
9
+ import {
10
+ DASHBOARD_COLLAB_SYNC_TIMEOUT_MS,
11
+ queueDashboardCollabSync,
12
+ } from "../server/lib/dashboard-collab-sync";
14
13
  import {
15
14
  getDashboard,
16
15
  upsertDashboardWithRetry,
@@ -171,24 +170,6 @@ function cloneConfig(config: Record<string, unknown>): Record<string, unknown> {
171
170
  return JSON.parse(JSON.stringify(config)) as Record<string, unknown>;
172
171
  }
173
172
 
174
- async function syncToCollab(
175
- dashboardId: string,
176
- config: Record<string, unknown>,
177
- ): Promise<void> {
178
- const docId = `dash-${dashboardId}`;
179
- const configStr = JSON.stringify(config);
180
- try {
181
- const exists = await hasCollabState(docId);
182
- if (exists) {
183
- await applyText(docId, configStr, "content", "agent");
184
- } else {
185
- await seedFromText(docId, configStr);
186
- }
187
- } catch {
188
- // SQL remains the source of truth; live collab sync is best-effort.
189
- }
190
- }
191
-
192
173
  function sqlValidationScope(
193
174
  operations: DashboardMutationOperation[],
194
175
  ): ReadonlySet<string> | "all" | null {
@@ -399,7 +380,10 @@ export default defineAction({
399
380
  // structurally identical to the winning attempt's `root`, but reflects
400
381
  // exactly what was saved.
401
382
  root = saved.config as Record<string, unknown>;
402
- await syncToCollab(dashboardId, root);
383
+ // SQL is the durable source of truth. Collab sync is a convenience for
384
+ // already-open editors, so it must never hold the saved mutation hostage
385
+ // behind an unavailable database or a stale per-document write lock.
386
+ queueDashboardCollabSync(dashboardId, root, "agent");
403
387
  }
404
388
 
405
389
  const compact = compactDashboardResult(root, movedPanelIdsFrom(operations));
@@ -421,6 +405,14 @@ export default defineAction({
421
405
  insertedPanelIds: mutation.insertedPanelIds,
422
406
  removedPanelIds: mutation.removedPanelIds,
423
407
  dashboardFieldsChanged: mutation.dashboardFieldsChanged,
408
+ ...(args.dryRun === true
409
+ ? { collabSync: { status: "skipped" as const } }
410
+ : {
411
+ collabSync: {
412
+ status: "queued" as const,
413
+ timeoutMs: DASHBOARD_COLLAB_SYNC_TIMEOUT_MS,
414
+ },
415
+ }),
424
416
  ...(args.returnConfig === true ? { config: root } : {}),
425
417
  ...(args.returnTypes === true
426
418
  ? {
@@ -1,15 +1,11 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import {
3
- hasCollabState,
4
- applyText,
5
- seedFromText,
6
- } from "@agent-native/core/collab";
7
2
  import {
8
3
  getRequestUserEmail,
9
4
  getRequestOrgId,
10
5
  } from "@agent-native/core/server";
11
6
  import { z } from "zod";
12
7
 
8
+ import { queueDashboardCollabSync } from "../server/lib/dashboard-collab-sync";
13
9
  import { upsertDashboardWithRetry } from "../server/lib/dashboards-store";
14
10
 
15
11
  function resolveScope() {
@@ -19,24 +15,6 @@ function resolveScope() {
19
15
  return { orgId, email };
20
16
  }
21
17
 
22
- async function syncToCollab(
23
- dashboardId: string,
24
- config: Record<string, unknown>,
25
- requestSource?: string,
26
- ): Promise<void> {
27
- const docId = `dash-${dashboardId}`;
28
- const configStr = JSON.stringify(config);
29
- try {
30
- if (await hasCollabState(docId)) {
31
- await applyText(docId, configStr, "content", requestSource);
32
- } else {
33
- await seedFromText(docId, configStr);
34
- }
35
- } catch {
36
- // Best-effort: SQL remains the source of truth.
37
- }
38
- }
39
-
40
18
  export default defineAction({
41
19
  description: "Rename a saved analytics dashboard by ID.",
42
20
  schema: z.object({
@@ -54,7 +32,7 @@ export default defineAction({
54
32
  const updated = await upsertDashboardWithRetry(args.id, ctx, (existing) => {
55
33
  return { kind: existing.kind, body: { ...existing.config, name } };
56
34
  });
57
- await syncToCollab(
35
+ queueDashboardCollabSync(
58
36
  args.id,
59
37
  updated.config,
60
38
  actionContext?.caller === "frontend" ? undefined : "agent",
@@ -1,9 +1,4 @@
1
1
  import { defineAction, embedApp } from "@agent-native/core";
2
- import {
3
- applyText,
4
- hasCollabState,
5
- seedFromText,
6
- } from "@agent-native/core/collab";
7
2
  import {
8
3
  buildDeepLink,
9
4
  getRequestOrgId,
@@ -11,6 +6,7 @@ import {
11
6
  } from "@agent-native/core/server";
12
7
  import { z } from "zod";
13
8
 
9
+ import { queueDashboardCollabSync } from "../server/lib/dashboard-collab-sync";
14
10
  import { upsertDashboardWithRetry } from "../server/lib/dashboards-store";
15
11
  import {
16
12
  compactDashboardResult,
@@ -74,24 +70,6 @@ function resolveTarget(args: {
74
70
  return { position: args.position ?? "top" };
75
71
  }
76
72
 
77
- async function syncToCollab(
78
- dashboardId: string,
79
- config: Record<string, unknown>,
80
- ): Promise<void> {
81
- const docId = `dash-${dashboardId}`;
82
- const configStr = JSON.stringify(config);
83
- try {
84
- const exists = await hasCollabState(docId);
85
- if (exists) {
86
- await applyText(docId, configStr, "content", "agent");
87
- } else {
88
- await seedFromText(docId, configStr);
89
- }
90
- } catch {
91
- // SQL remains the source of truth; live collab sync is best-effort.
92
- }
93
- }
94
-
95
73
  export default defineAction({
96
74
  agentTool: false,
97
75
  description:
@@ -172,7 +150,7 @@ export default defineAction({
172
150
  },
173
151
  );
174
152
  const root = saved.config as Record<string, unknown>;
175
- await syncToCollab(dashboardId, root);
153
+ queueDashboardCollabSync(dashboardId, root, "agent");
176
154
 
177
155
  const compact = compactDashboardResult(root, orderResult.movedPanelIds);
178
156
  return {
@@ -1,15 +1,11 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import {
3
- applyText,
4
- hasCollabState,
5
- seedFromText,
6
- } from "@agent-native/core/collab";
7
2
  import {
8
3
  getRequestOrgId,
9
4
  getRequestUserEmail,
10
5
  } from "@agent-native/core/server";
11
6
  import { z } from "zod";
12
7
 
8
+ import { queueDashboardCollabSync } from "../server/lib/dashboard-collab-sync";
13
9
  import { restoreDashboardRevision } from "../server/lib/dashboards-store";
14
10
 
15
11
  function resolveScope() {
@@ -19,25 +15,6 @@ function resolveScope() {
19
15
  return { orgId, email };
20
16
  }
21
17
 
22
- async function syncToCollab(
23
- dashboardId: string,
24
- config: Record<string, unknown>,
25
- requestSource?: string,
26
- ): Promise<void> {
27
- const docId = `dash-${dashboardId}`;
28
- const configStr = JSON.stringify(config);
29
- try {
30
- const exists = await hasCollabState(docId);
31
- if (exists) {
32
- await applyText(docId, configStr, "content", requestSource);
33
- } else {
34
- await seedFromText(docId, configStr);
35
- }
36
- } catch {
37
- // SQL is the source of truth; open editors also refetch on the dashboards signal.
38
- }
39
- }
40
-
41
18
  export default defineAction({
42
19
  description:
43
20
  "Restore a dashboard to a saved history revision, snapshotting the current dashboard first.",
@@ -63,7 +40,7 @@ export default defineAction({
63
40
  );
64
41
  }
65
42
  const { dashboard, snapshotRevisionId } = restored;
66
- await syncToCollab(
43
+ queueDashboardCollabSync(
67
44
  dashboard.id,
68
45
  dashboard.config,
69
46
  actionContext?.caller === "frontend" ? undefined : "agent",
@@ -1,9 +1,4 @@
1
1
  import { defineAction, embedApp } from "@agent-native/core";
2
- import {
3
- hasCollabState,
4
- applyText,
5
- seedFromText,
6
- } from "@agent-native/core/collab";
7
2
  import {
8
3
  getRequestUserEmail,
9
4
  getRequestOrgId,
@@ -13,6 +8,7 @@ import { z } from "zod";
13
8
 
14
9
  import { interpolate } from "../app/pages/adhoc/sql-dashboard/interpolate";
15
10
  import { dryRunQuery } from "../server/lib/bigquery";
11
+ import { queueDashboardCollabSync } from "../server/lib/dashboard-collab-sync";
16
12
  import { validateFirstPartyDashboardTimeScope } from "../server/lib/dashboard-time-scope";
17
13
  import {
18
14
  upsertDashboard,
@@ -536,30 +532,6 @@ function opCanChangePanelSql(op: JsonOp): boolean {
536
532
  );
537
533
  }
538
534
 
539
- /**
540
- * Push a config update through the collab layer so open dashboard editors
541
- * receive the change in real time. Seeds the collab state if it doesn't
542
- * exist yet (e.g. dashboard was created before the collab plugin was added).
543
- */
544
- async function syncToCollab(
545
- dashboardId: string,
546
- config: Record<string, unknown>,
547
- requestSource?: string,
548
- ): Promise<void> {
549
- const docId = `dash-${dashboardId}`;
550
- const configStr = JSON.stringify(config);
551
- try {
552
- const exists = await hasCollabState(docId);
553
- if (exists) {
554
- await applyText(docId, configStr, "content", requestSource);
555
- } else {
556
- await seedFromText(docId, configStr);
557
- }
558
- } catch {
559
- // Collab sync is best-effort — the SQL write is the source of truth
560
- }
561
- }
562
-
563
535
  function isAgentCaller(caller: string | undefined): boolean {
564
536
  return caller === "tool" || caller === "mcp" || caller === "a2a";
565
537
  }
@@ -642,7 +614,7 @@ export default defineAction({
642
614
  const sqlError = await validatePanelSql(args.config);
643
615
  if (sqlError) throw new Error(sqlError);
644
616
  await upsertDashboard(dashboardId, "sql", args.config, ctx);
645
- await syncToCollab(
617
+ queueDashboardCollabSync(
646
618
  dashboardId,
647
619
  args.config,
648
620
  isAgentCaller(actionContext?.caller) ? "agent" : undefined,
@@ -675,7 +647,7 @@ export default defineAction({
675
647
  },
676
648
  );
677
649
  const root = saved.config as Record<string, unknown>;
678
- await syncToCollab(
650
+ queueDashboardCollabSync(
679
651
  dashboardId,
680
652
  root,
681
653
  isAgentCaller(actionContext?.caller) ? "agent" : undefined,
@@ -721,7 +693,7 @@ export default defineAction({
721
693
  },
722
694
  );
723
695
  const root = saved.config as Record<string, unknown>;
724
- await syncToCollab(
696
+ queueDashboardCollabSync(
725
697
  dashboardId,
726
698
  root,
727
699
  isAgentCaller(actionContext?.caller) ? "agent" : undefined,
@@ -1,6 +1,6 @@
1
1
  import { ChangelogDialog } from "@agent-native/core/client/changelog";
2
2
  import { callAction, useChangeVersions } from "@agent-native/core/client/hooks";
3
- import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
3
+ import { useT } from "@agent-native/core/client/i18n";
4
4
  import { useOrgRole } from "@agent-native/core/client/org";
5
5
  import {
6
6
  IconFlask,
@@ -11,7 +11,6 @@ import {
11
11
  IconMoon,
12
12
  IconHistory,
13
13
  IconHierarchy2,
14
- IconLanguage,
15
14
  IconRefresh,
16
15
  IconSettings,
17
16
  } from "@tabler/icons-react";
@@ -41,13 +40,6 @@ import {
41
40
  CommandGroup,
42
41
  CommandItem,
43
42
  } from "@/components/ui/command";
44
- import {
45
- Dialog,
46
- DialogContent,
47
- DialogHeader,
48
- DialogTitle,
49
- } from "@/components/ui/dialog";
50
- import { Label } from "@/components/ui/label";
51
43
  import { Skeleton } from "@/components/ui/skeleton";
52
44
  import { useReplayStorageStatus } from "@/hooks/use-replay-storage-status";
53
45
  import { dashboardCacheScope } from "@/lib/prefetch-keys";
@@ -263,7 +255,6 @@ export function CommandPalette() {
263
255
  const dashboardScope = dashboardCacheScope(auth);
264
256
  const [open, setOpen] = useState(false);
265
257
  const [changelogOpen, setChangelogOpen] = useState(false);
266
- const [languageOpen, setLanguageOpen] = useState(false);
267
258
  const [searchQuery, setSearchQuery] = useState("");
268
259
  const [selectedCommand, setSelectedCommand] = useState("");
269
260
  const commandListRef = useRef<HTMLDivElement>(null);
@@ -553,25 +544,6 @@ export function CommandPalette() {
553
544
  key="appearance"
554
545
  heading={t("commandPalette.groupAppearance")}
555
546
  >
556
- <CommandItem
557
- value={`appearance:language:${t("settings.languageTitle")}`}
558
- onSelect={() => {
559
- setOpen(false);
560
- setLanguageOpen(true);
561
- }}
562
- keywords={commandPaletteKeywords(
563
- t("settings.languageTitle"),
564
- t("settings.languageLabel"),
565
- "language",
566
- "locale",
567
- "translation",
568
- "internationalization",
569
- "i18n",
570
- )}
571
- >
572
- <IconLanguage className="me-2 h-4 w-4 text-muted-foreground" />
573
- {t("settings.languageTitle")}
574
- </CommandItem>
575
547
  <CommandItem
576
548
  value={`appearance:theme:${
577
549
  isDark
@@ -678,17 +650,6 @@ export function CommandPalette() {
678
650
  onOpenChange={setChangelogOpen}
679
651
  markdown={changelog}
680
652
  />
681
- <Dialog open={languageOpen} onOpenChange={setLanguageOpen}>
682
- <DialogContent className="max-w-sm">
683
- <DialogHeader>
684
- <DialogTitle>{t("settings.languageTitle")}</DialogTitle>
685
- </DialogHeader>
686
- <div className="space-y-1.5">
687
- <Label>{t("settings.languageLabel")}</Label>
688
- <LanguagePicker label={t("settings.languageLabel")} />
689
- </div>
690
- </DialogContent>
691
- </Dialog>
692
653
  </>
693
654
  );
694
655
  }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: changed
3
+ date: 2026-08-07
4
+ ---
5
+
6
+ Language selection now lives in Settings instead of the command menu.
@@ -18,6 +18,13 @@ AGENT_PROD_CODE_EXECUTION = "sandboxed"
18
18
  # turns on the durable background worker instead of the short foreground wall.
19
19
  AGENT_CHAT_DURABLE_BACKGROUND = "true"
20
20
 
21
+ [context.production.environment]
22
+ # Analytics owns its schema at release time (`migrate:production` above), so
23
+ # production request functions skip migrations. Preview functions still run
24
+ # request-path migrations because previews do not run the production release
25
+ # migration step.
26
+ AGENT_NATIVE_RELEASE_MIGRATIONS = "1"
27
+
21
28
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
22
29
  # Hosted agent chat uses a soft timeout before this function timeout so the
23
30
  # hidden auto-continuation path can hand off before Netlify kills the function.