@agent-native/core-corpus 0.210.2 → 0.211.0

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 (192) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +5 -3
  3. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
  4. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
  5. package/corpus/templates/analytics/CHANGELOG.md +18 -0
  6. package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
  7. package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
  8. package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
  9. package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
  10. package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
  11. package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
  12. package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
  13. package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
  14. package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
  15. package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
  16. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
  17. package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
  18. package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
  19. package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
  20. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
  21. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
  22. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
  23. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
  24. package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
  25. package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
  26. package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
  27. package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
  28. package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
  29. package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
  30. package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
  31. package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
  32. package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
  33. package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
  34. package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
  35. package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
  37. package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
  38. package/corpus/templates/analytics/server/db/schema.ts +1 -0
  39. package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
  40. package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
  41. package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
  42. package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
  43. package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
  44. package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
  45. package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
  46. package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
  47. package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
  48. package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
  49. package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
  50. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
  51. package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
  52. package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
  53. package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
  54. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
  55. package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
  56. package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
  57. package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
  58. package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
  59. package/corpus/templates/analytics/server/plugins/db.ts +9 -1
  60. package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
  61. package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
  62. package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
  63. package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
  64. package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
  65. package/corpus/templates/calendar/actions/delete-event.ts +1 -3
  66. package/corpus/templates/calendar/actions/delete-events.ts +1 -2
  67. package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
  68. package/corpus/templates/calendar/actions/update-event.ts +2 -6
  69. package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
  70. package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
  71. package/corpus/templates/clips/CHANGELOG.md +11 -24
  72. package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
  73. package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
  74. package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
  75. package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
  76. package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
  77. package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
  78. package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
  79. package/corpus/templates/clips/app/root.tsx +22 -10
  80. package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
  81. package/corpus/templates/clips/app/routes/import.tsx +115 -110
  82. package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
  83. package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
  84. package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
  86. package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
  87. package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
  88. package/corpus/templates/clips/desktop/src/app.tsx +29 -186
  89. package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
  90. package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
  91. package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
  92. package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
  93. package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
  94. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
  95. package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
  96. package/corpus/templates/content/CHANGELOG.md +12 -0
  97. package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
  98. package/corpus/templates/content/actions/_database-navigation.ts +3 -3
  99. package/corpus/templates/content/actions/_database-utils.ts +10 -22
  100. package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
  101. package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
  102. package/corpus/templates/content/actions/export-document.ts +2 -14
  103. package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
  104. package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
  105. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
  106. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
  107. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
  108. package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
  109. package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
  110. package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
  111. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
  112. package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
  113. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
  114. package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
  115. package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
  116. package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
  117. package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
  118. package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
  119. package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
  120. package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
  121. package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
  122. package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
  123. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
  124. package/corpus/templates/content/app/global.css +23 -0
  125. package/corpus/templates/content/app/i18n-data.ts +1 -0
  126. package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
  127. package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
  128. package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
  129. package/corpus/templates/content/changelog/2026-10-19-edits-made-from-a-block-s-menu-or-a-table-s-row-and-column-m.md +6 -0
  130. package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
  131. package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
  132. package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
  133. package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
  134. package/corpus/templates/content/e2e/helpers.ts +10 -0
  135. package/corpus/templates/content/shared/api.ts +18 -12
  136. package/corpus/templates/content/shared/database-query.ts +107 -13
  137. package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
  138. package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
  139. package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
  140. package/corpus/templates/design/CHANGELOG.md +2 -0
  141. package/corpus/templates/design/actions/delete-file.ts +1 -4
  142. package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
  143. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
  144. package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
  145. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
  146. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
  147. package/corpus/templates/design/changelog/2026-10-19-reply-and-edit-on-a-comment-in-the-sidebar-or-on-a-canvas-pi.md +6 -0
  148. package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
  149. package/corpus/templates/design/e2e/global-setup.ts +185 -85
  150. package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
  151. package/corpus/templates/dispatch/CHANGELOG.md +4 -0
  152. package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
  153. package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
  154. package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
  155. package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
  156. package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
  157. package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
  158. package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
  159. package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
  160. package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
  161. package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
  162. package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
  163. package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
  164. package/corpus/templates/mail/AGENTS.md +1 -1
  165. package/corpus/templates/mail/CHANGELOG.md +14 -0
  166. package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
  167. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
  168. package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
  169. package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
  170. package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
  171. package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
  172. package/corpus/templates/mail/app/root.tsx +10 -2
  173. package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
  174. package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
  175. package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
  176. package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
  177. package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
  178. package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
  179. package/corpus/templates/mail/server/handlers/emails.ts +9 -0
  180. package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
  181. package/corpus/templates/plan/CHANGELOG.md +4 -0
  182. package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
  183. package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
  184. package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
  185. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
  186. package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
  187. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
  188. package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
  189. package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
  190. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
  191. package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
  192. package/package.json +1 -1
@@ -1,5 +1,7 @@
1
1
  import { defineAction } from "@agent-native/core/action";
2
2
  import { buildDeepLink } from "@agent-native/core/server";
3
+ import { getUserProfiles } from "@agent-native/core/user-profile/server";
4
+ import { isEmailDerivedName } from "@agent-native/core/user-profile/shared";
3
5
  import { z } from "zod";
4
6
 
5
7
  import {
@@ -12,7 +14,23 @@ import {
12
14
  requireSigmaMigrationContext,
13
15
  sigmaMigrationSetupLink,
14
16
  } from "../server/lib/bi-migration/sigma-migration.js";
15
- import { loadAccessibleDashboardMetadataByIds } from "../server/lib/dashboards-store.js";
17
+ import {
18
+ loadAccessibleDashboardMetadataByIds,
19
+ loadOrgDashboardOwnersByIds,
20
+ } from "../server/lib/dashboards-store.js";
21
+
22
+ // One Sigma page covers most orgs; mapping and dashboard reads take at most
23
+ // 100 ids each, so they run in chunks.
24
+ const WORKBOOK_PAGE_SIZE = 1_000;
25
+ const ID_CHUNK_SIZE = 100;
26
+
27
+ function chunk<T>(items: readonly T[]): T[][] {
28
+ const chunks: T[][] = [];
29
+ for (let i = 0; i < items.length; i += ID_CHUNK_SIZE) {
30
+ chunks.push(items.slice(i, i + ID_CHUNK_SIZE));
31
+ }
32
+ return chunks;
33
+ }
16
34
 
17
35
  const listSchema = z.object({
18
36
  connectionId: z
@@ -33,7 +51,7 @@ const listSchema = z.object({
33
51
 
34
52
  export default defineAction({
35
53
  description:
36
- "List Sigma workbooks accessible to the granted Analytics Sigma connection. Returns workbook metadata, preview status, retry eligibility, and a dashboard link only when the current user owns and can access the imported dashboard. Sigma workbook imports remain disabled until the source project mapping can be verified.",
54
+ "List Sigma workbooks accessible to the granted Analytics Sigma connection. Only workbooks the signed-in Sigma user can access are returned. Each workbook includes its migration status; a migrated workbook includes the Analytics dashboard id, link, and owner email and display name for every org member, plus canOpen and the dashboard title only when the caller can access that dashboard. Migrate an unmigrated workbook; open a migrated one instead of importing it again. Sigma workbook imports remain disabled until the source project mapping can be verified.",
37
55
  schema: listSchema,
38
56
  http: { method: "GET" },
39
57
  readOnly: true,
@@ -60,58 +78,107 @@ export default defineAction({
60
78
  const page = await listSigmaWorkbooks({
61
79
  connectionId: selection.selected.id,
62
80
  page: args.page,
63
- limit: 100,
64
- });
65
- const migrations = await listExternalDashboards({
66
- provider: "sigma",
67
- sourceIds: page.workbooks.map((workbook) => workbook.workbookId),
68
- limit: 100,
81
+ limit: WORKBOOK_PAGE_SIZE,
69
82
  });
83
+ // Mapping rows are read only for workbooks Sigma returned to this user, so
84
+ // nobody learns about a migration of a workbook they cannot see in Sigma.
85
+ const migrations = (
86
+ await Promise.all(
87
+ chunk(page.workbooks.map((workbook) => workbook.workbookId)).map(
88
+ (sourceIds) =>
89
+ listExternalDashboards({
90
+ provider: "sigma",
91
+ sourceIds,
92
+ limit: ID_CHUNK_SIZE,
93
+ }),
94
+ ),
95
+ )
96
+ ).flat();
70
97
  const migrationBySourceId = new Map(
71
98
  migrations.map((migration) => [migration.sourceId, migration]),
72
99
  );
73
100
  const dashboardIds = migrations.flatMap((migration) =>
74
- migration.status === "migrated" &&
75
- migration.migratedByCurrentUser &&
76
- migration.targetDashboardId
101
+ migration.status === "migrated" && migration.targetDashboardId
77
102
  ? [migration.targetDashboardId]
78
103
  : [],
79
104
  );
80
- const dashboards = await loadAccessibleDashboardMetadataByIds(
81
- { email: context.userEmail, orgId: context.orgId },
82
- dashboardIds,
83
- );
105
+ const [dashboards, owners] = await Promise.all([
106
+ Promise.all(
107
+ chunk(dashboardIds).map((ids) =>
108
+ loadAccessibleDashboardMetadataByIds(
109
+ { email: context.userEmail, orgId: context.orgId },
110
+ ids,
111
+ ),
112
+ ),
113
+ ).then((results) => results.flat()),
114
+ Promise.all(
115
+ chunk(dashboardIds).map((ids) =>
116
+ loadOrgDashboardOwnersByIds(context.orgId, ids),
117
+ ),
118
+ ).then((results) => results.flat()),
119
+ ]);
84
120
  const dashboardById = new Map(
85
121
  dashboards.map((dashboard) => [dashboard.id, dashboard]),
86
122
  );
123
+ const ownerById = new Map(
124
+ owners
125
+ .filter((owner) => owner.kind === "sql")
126
+ .map((owner) => [owner.id, owner]),
127
+ );
128
+
129
+ const ownerEmailOf = (sourceId: string): string | null => {
130
+ const migration = migrationBySourceId.get(sourceId);
131
+ if (migration?.status === "migrated") {
132
+ const owner = migration.targetDashboardId
133
+ ? ownerById.get(migration.targetDashboardId)
134
+ : undefined;
135
+ return owner?.ownerEmail.toLowerCase() ?? migration.migratedByEmail;
136
+ }
137
+ return migration?.status === "migrating"
138
+ ? migration.migratedByEmail
139
+ : null;
140
+ };
141
+ // Names for the owner avatars, in one batch so the table never looks
142
+ // people up row by row.
143
+ const profiles = await getUserProfiles(
144
+ page.workbooks.flatMap((workbook) => {
145
+ const email = ownerEmailOf(workbook.workbookId);
146
+ return email ? [email] : [];
147
+ }),
148
+ );
149
+ const displayNameOf = (email: string): string | null => {
150
+ const name = profiles.get(email.toLowerCase())?.name?.trim();
151
+ return name && !isEmailDerivedName(name, email) ? name : null;
152
+ };
87
153
 
88
154
  const workbooks = page.workbooks.map((workbook) => {
89
155
  const migration = migrationBySourceId.get(workbook.workbookId);
90
156
  const status: ExternalDashboardMigrationStatus =
91
157
  migration?.status ?? "not_started";
92
- const migrationDashboard =
93
- migration?.status === "migrated" &&
94
- migration.migratedByCurrentUser &&
95
- migration.targetDashboardId
96
- ? dashboardById.get(migration.targetDashboardId)
158
+ const targetDashboardId =
159
+ migration?.status === "migrated"
160
+ ? (migration.targetDashboardId ?? undefined)
97
161
  : undefined;
98
- let dashboardId: string | undefined;
99
- let dashboardName: string | undefined;
100
- if (
101
- migration?.status === "migrated" &&
102
- migration.migratedByCurrentUser &&
103
- migration.targetDashboardId
104
- ) {
105
- const dashboard = dashboardById.get(migration.targetDashboardId);
106
- if (
107
- dashboard?.kind === "sql" &&
108
- dashboard.ownerEmail.toLowerCase() === context.userEmail &&
109
- dashboard.orgId === context.orgId
110
- ) {
111
- dashboardId = dashboard.id;
112
- dashboardName = dashboard.title;
113
- }
114
- }
162
+ const owner = targetDashboardId
163
+ ? ownerById.get(targetDashboardId)
164
+ : undefined;
165
+ const accessible = targetDashboardId
166
+ ? dashboardById.get(targetDashboardId)
167
+ : undefined;
168
+ const canOpen = Boolean(
169
+ owner &&
170
+ accessible?.kind === "sql" &&
171
+ accessible.orgId === context.orgId,
172
+ );
173
+ const dashboardId = owner ? owner.id : undefined;
174
+ const migratedOwnerEmail =
175
+ migration?.status === "migrated"
176
+ ? ownerEmailOf(workbook.workbookId)
177
+ : null;
178
+ const migratingByEmail =
179
+ migration?.status === "migrating"
180
+ ? ownerEmailOf(workbook.workbookId)
181
+ : null;
115
182
  return {
116
183
  id: workbook.workbookId,
117
184
  name: workbook.name,
@@ -124,28 +191,42 @@ export default defineAction({
124
191
  ...(dashboardId
125
192
  ? {
126
193
  dashboardId,
127
- dashboardName,
194
+ ...(canOpen && accessible
195
+ ? { dashboardName: accessible.title }
196
+ : {}),
128
197
  dashboardUrl: buildDeepLink({
129
198
  app: "analytics",
130
199
  view: "adhoc",
131
200
  params: { dashboardId },
132
201
  }),
202
+ canOpen,
203
+ }
204
+ : {}),
205
+ ...(migratedOwnerEmail
206
+ ? {
207
+ migratedOwnerEmail,
208
+ migratedOwnerName: displayNameOf(migratedOwnerEmail),
133
209
  }
134
210
  : {}),
135
211
  ...(migration?.status === "migrated" &&
136
212
  migration.migratedByCurrentUser &&
137
- migration.targetDashboardId &&
138
- !migrationDashboard
213
+ targetDashboardId &&
214
+ !owner
139
215
  ? { canRecoverMissingDashboard: true }
140
216
  : {}),
217
+ ...(migratingByEmail
218
+ ? {
219
+ migratingByEmail,
220
+ migratingByName: displayNameOf(migratingByEmail),
221
+ }
222
+ : {}),
223
+ canStartImport: migration?.canStartImport ?? true,
141
224
  ...(migration?.status === "migrating" && migration.canRetryStaleClaim
142
225
  ? { canRetryStaleClaim: true }
143
226
  : {}),
144
227
  ...(migration?.flaggedCount
145
228
  ? { flaggedCount: migration.flaggedCount }
146
229
  : {}),
147
- retryableByCurrentUser:
148
- status !== "failed" || migration?.migratedByCurrentUser === true,
149
230
  ...(migration?.status === "migrated" && !migration.migratedByCurrentUser
150
231
  ? { migratedByTeammate: true }
151
232
  : {}),
@@ -14,7 +14,6 @@ import {
14
14
  boundExternalDashboardDiagnostics,
15
15
  completeExternalDashboard,
16
16
  createExternalDashboard,
17
- failExternalDashboard,
18
17
  listExternalDashboards,
19
18
  type ExternalDashboardElementDiagnostic,
20
19
  } from "../server/lib/bi-migration/external-dashboards-store.js";
@@ -29,6 +28,7 @@ import {
29
28
  requireSigmaMigrationContext,
30
29
  sigmaMigrationSetupLink,
31
30
  } from "../server/lib/bi-migration/sigma-migration.js";
31
+ import { abandonSigmaImport } from "../server/lib/bi-migration/unfinished-import.js";
32
32
  import {
33
33
  createDashboardFolder,
34
34
  listDashboardFolders,
@@ -431,13 +431,14 @@ export default defineAction({
431
431
  sourceIds: [args.workbookId],
432
432
  limit: 1,
433
433
  });
434
- const dashboard =
435
- migration?.migratedByCurrentUser && migration.targetDashboardId
436
- ? await getDashboard(migration.targetDashboardId, {
437
- email: context.email,
438
- orgId: context.orgId,
439
- })
440
- : null;
434
+ // getDashboard applies the caller's access, so a teammate gets the
435
+ // link only when they can open the existing dashboard.
436
+ const dashboard = migration?.targetDashboardId
437
+ ? await getDashboard(migration.targetDashboardId, {
438
+ email: context.email,
439
+ orgId: context.orgId,
440
+ })
441
+ : null;
441
442
  return {
442
443
  status: "already_migrated" as const,
443
444
  ...(dashboard?.kind === "sql"
@@ -455,6 +456,11 @@ export default defineAction({
455
456
  let failureCode: "dashboard_write_failed" | "verification_failed" =
456
457
  "dashboard_write_failed";
457
458
  let dashboardWriteSucceeded = false;
459
+ const id = dashboardIdFor({
460
+ orgId: context.orgId,
461
+ userEmail: context.userEmail,
462
+ workbookId: args.workbookId,
463
+ });
458
464
  try {
459
465
  const installedAt = new Date().toISOString();
460
466
  const compiled = compileSigmaDashboard({
@@ -462,11 +468,6 @@ export default defineAction({
462
468
  review,
463
469
  installedAt,
464
470
  });
465
- const id = dashboardIdFor({
466
- orgId: context.orgId,
467
- userEmail: context.userEmail,
468
- workbookId: args.workbookId,
469
- });
470
471
  const existing = await getDashboard(id, {
471
472
  email: context.email,
472
473
  orgId: context.orgId,
@@ -560,12 +561,14 @@ export default defineAction({
560
561
  _receipt,
561
562
  };
562
563
  } catch (error) {
563
- await failExternalDashboard({
564
- provider: "sigma",
565
- sourceId: args.workbookId,
564
+ await abandonSigmaImport({
565
+ workbookId: args.workbookId,
566
566
  claimToken: claim.claimToken,
567
567
  failureCode,
568
- preservePreviousMigration: !dashboardWriteSucceeded,
568
+ dashboardId: id,
569
+ wroteDashboard: dashboardWriteSucceeded,
570
+ previousDashboardId: claim.record.targetDashboardId,
571
+ ctx: { email: context.email, orgId: context.orgId },
569
572
  });
570
573
  throw error;
571
574
  }
@@ -19,6 +19,10 @@ export default defineAction({
19
19
  schema: z.object({
20
20
  id: z.string().describe("Public key row id to revoke."),
21
21
  }),
22
+ // It creates, changes, or revokes a credential or who may use one: every
23
+ // call needs the user's approval, and "always allow" is not offered.
24
+ needsApproval: true,
25
+ allowPersistentApproval: false,
22
26
  run: async (args) => {
23
27
  return revokeAnalyticsPublicKey(resolveScope(), args.id);
24
28
  },