@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,6 +1,13 @@
1
1
  import { useT } from "@agent-native/core/client/i18n";
2
+ import {
3
+ useCreateMcpServer,
4
+ useMcpServers,
5
+ type CreateMcpServerArgs,
6
+ } from "@agent-native/core/client/resources";
7
+ import { buildSettingsRoute } from "@agent-native/core/navigation";
2
8
  import { useSendToAgentChat } from "@agent-native/toolkit/app/chat/composer";
3
9
  import { ExtensionSlot } from "@agent-native/toolkit/app/extensions";
10
+ import { McpIntegrationDialog } from "@agent-native/toolkit/app/resources";
4
11
  import {
5
12
  IconPlus,
6
13
  IconCheck,
@@ -8,8 +15,10 @@ import {
8
15
  IconChevronLeft,
9
16
  IconArrowUp,
10
17
  } from "@tabler/icons-react";
11
- import { useState, useEffect, useRef } from "react";
18
+ import { useCallback, useState, useEffect, useRef } from "react";
19
+ import { useNavigate } from "react-router";
12
20
 
21
+ import { Button } from "@/components/ui/button";
13
22
  import {
14
23
  DropdownMenu,
15
24
  DropdownMenuTrigger,
@@ -36,6 +45,10 @@ import {
36
45
  usePylonContact,
37
46
  isAuthError,
38
47
  } from "@/hooks/use-integrations";
48
+ import {
49
+ canManageGongConnection,
50
+ hasConnectedGongMcpServer,
51
+ } from "@/lib/gong-connection";
39
52
  import type { MailIntegrationStatuses } from "@/lib/integration-status";
40
53
  import { isMcpEmbedSurface } from "@/lib/mcp-embed";
41
54
  import { cn } from "@/lib/utils";
@@ -58,9 +71,11 @@ interface IntegrationDef {
58
71
  id: ProviderId;
59
72
  name: string;
60
73
  descriptionKey: string;
61
- keyPlaceholder: string;
62
- helpUrl: string;
63
- helpSteps: string[];
74
+ keyPlaceholder?: string;
75
+ keyPlaceholderKey?: string;
76
+ helpUrl?: string;
77
+ helpSteps?: string[];
78
+ helpStepKeys?: string[];
64
79
  logo: React.ReactNode;
65
80
  }
66
81
 
@@ -131,13 +146,9 @@ const INTEGRATIONS: IntegrationDef[] = [
131
146
  id: "gong",
132
147
  name: "Gong",
133
148
  descriptionKey: "mail.integrations.gongDescription",
134
- keyPlaceholder: "Gong API key or access_key:secret...",
149
+ keyPlaceholderKey: "mail.integrations.gongKeyPlaceholder",
135
150
  helpUrl: "https://app.gong.io/company/api",
136
- helpSteps: [
137
- "Go to Gong > Company Settings > API",
138
- "Generate API credentials",
139
- "Copy the access key (or key:secret)",
140
- ],
151
+ helpStepKeys: ["mail.integrations.gongKeyFormatHint"],
141
152
  logo: (
142
153
  <svg
143
154
  viewBox="0 0 42 42"
@@ -275,8 +286,35 @@ export function IntegrationsSidebar({
275
286
  }
276
287
 
277
288
  function IntegrationSetup({ statuses }: { statuses: MailIntegrationStatuses }) {
289
+ const t = useT();
290
+ const navigate = useNavigate();
278
291
  const [expanded, setExpanded] = useState(false);
279
292
  const [configuring, setConfiguring] = useState<ProviderId | null>(null);
293
+ const [gongDialogOpen, setGongDialogOpen] = useState(false);
294
+ const mcpServersQuery = useMcpServers({ defer: true });
295
+ const createMcpServer = useCreateMcpServer();
296
+ const mcpServers = mcpServersQuery.data;
297
+ const canManageGong = canManageGongConnection(mcpServers);
298
+ const canCreateOrgMcp = Boolean(mcpServers?.orgId && canManageGong);
299
+ const gongConnected = hasConnectedGongMcpServer(mcpServers);
300
+ const canRetryGongAccessCheck =
301
+ mcpServersQuery.isError && (!mcpServers || canManageGong);
302
+ const handleCreateMcpServer = useCallback(
303
+ async (args: CreateMcpServerArgs) => {
304
+ if (args.scope === "org" && !canCreateOrgMcp) {
305
+ throw new Error(t("mail.error.failedToConnect"));
306
+ }
307
+ return createMcpServer.mutateAsync(args);
308
+ },
309
+ [canCreateOrgMcp, createMcpServer, t],
310
+ );
311
+ const gongPermissionError = canRetryGongAccessCheck ? (
312
+ <GongPermissionLookupError
313
+ isRetrying={mcpServersQuery.isFetching}
314
+ onRetry={() => void mcpServersQuery.refetch()}
315
+ />
316
+ ) : null;
317
+ const showGong = canManageGong || statuses.gong || gongConnected;
280
318
 
281
319
  if (configuring) {
282
320
  const def = INTEGRATIONS.find((i) => i.id === configuring)!;
@@ -289,17 +327,19 @@ function IntegrationSetup({ statuses }: { statuses: MailIntegrationStatuses }) {
289
327
  const connectedCount = [
290
328
  statuses.apollo,
291
329
  statuses.hubspot,
292
- statuses.gong,
330
+ statuses.gong || gongConnected,
293
331
  statuses.pylon,
294
332
  ].filter(Boolean).length;
333
+ const visibleIntegrationCount = INTEGRATIONS.length - Number(!showGong);
295
334
  return (
296
335
  <div className="px-4 py-2">
336
+ {gongPermissionError}
297
337
  <button
298
338
  onClick={() => setExpanded(true)}
299
339
  className="text-[11px] text-muted-foreground/70 hover:text-muted-foreground transition-colors"
300
340
  >
301
341
  {connectedCount > 0
302
- ? `Integrations (${connectedCount}/${INTEGRATIONS.length})`
342
+ ? `Integrations (${connectedCount}/${visibleIntegrationCount})`
303
343
  : "Add integrations"}
304
344
  </button>
305
345
  </div>
@@ -308,6 +348,7 @@ function IntegrationSetup({ statuses }: { statuses: MailIntegrationStatuses }) {
308
348
 
309
349
  return (
310
350
  <div className="px-4 py-3">
351
+ {gongPermissionError}
311
352
  <div className="flex items-center justify-between mb-3">
312
353
  <h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wider">
313
354
  Integrations
@@ -320,19 +361,74 @@ function IntegrationSetup({ statuses }: { statuses: MailIntegrationStatuses }) {
320
361
  </button>
321
362
  </div>
322
363
  <div className="space-y-1.5">
323
- {INTEGRATIONS.map((def) => {
324
- const connected = statuses[def.id];
325
- return (
326
- <IntegrationRow
327
- key={def.id}
328
- def={def}
329
- connected={connected}
330
- onConfigure={() => setConfiguring(def.id)}
331
- />
332
- );
333
- })}
364
+ {INTEGRATIONS.filter((def) => def.id !== "gong" || showGong).map(
365
+ (def) => {
366
+ const connected =
367
+ def.id === "gong"
368
+ ? statuses.gong || gongConnected
369
+ : statuses[def.id];
370
+ return (
371
+ <IntegrationRow
372
+ key={def.id}
373
+ def={def}
374
+ connected={connected}
375
+ mcpConnected={def.id === "gong" && gongConnected}
376
+ legacyConnected={def.id === "gong" && statuses.gong}
377
+ canManageMcpSetup={def.id !== "gong" || canManageGong}
378
+ onManage={() =>
379
+ void navigate(buildSettingsRoute("integrations"))
380
+ }
381
+ onConfigure={() => {
382
+ if (def.id === "gong") setGongDialogOpen(true);
383
+ else setConfiguring(def.id);
384
+ }}
385
+ onLegacyConfigure={
386
+ def.id === "gong" ? () => setConfiguring("gong") : undefined
387
+ }
388
+ />
389
+ );
390
+ },
391
+ )}
334
392
  <AddIntegrationButton />
335
393
  </div>
394
+ <McpIntegrationDialog
395
+ open={gongDialogOpen}
396
+ onOpenChange={setGongDialogOpen}
397
+ initialIntegrationId={gongDialogOpen ? "gong" : null}
398
+ defaultScope="org"
399
+ canCreateOrgMcp={canCreateOrgMcp}
400
+ hasOrg={Boolean(mcpServers?.orgId)}
401
+ onCreateMcpServer={handleCreateMcpServer}
402
+ onCreated={() => void mcpServersQuery.refetch()}
403
+ />
404
+ </div>
405
+ );
406
+ }
407
+
408
+ function GongPermissionLookupError({
409
+ isRetrying,
410
+ onRetry,
411
+ }: {
412
+ isRetrying: boolean;
413
+ onRetry: () => void;
414
+ }) {
415
+ const t = useT();
416
+
417
+ return (
418
+ <div className="mb-2 flex items-center justify-between gap-2" role="alert">
419
+ <p className="text-[11px] text-muted-foreground">
420
+ {t("mail.integrations.gongAccessCheckFailed")}
421
+ </p>
422
+ <Button
423
+ type="button"
424
+ variant="ghost"
425
+ size="sm"
426
+ className="h-auto shrink-0 px-2 py-1 text-[11px]"
427
+ onClick={onRetry}
428
+ disabled={isRetrying}
429
+ >
430
+ {t(isRetrying ? "mail.error.retrying" : "mail.error.tryAgain")}
431
+ </Button>
336
432
  </div>
337
433
  );
338
434
  }
@@ -450,14 +546,26 @@ function AddIntegrationButton() {
450
546
  function IntegrationRow({
451
547
  def,
452
548
  connected,
549
+ mcpConnected = false,
550
+ legacyConnected = false,
551
+ canManageMcpSetup = true,
552
+ onManage,
453
553
  onConfigure,
554
+ onLegacyConfigure,
454
555
  }: {
455
556
  def: IntegrationDef;
456
557
  connected: boolean;
558
+ mcpConnected?: boolean;
559
+ legacyConnected?: boolean;
560
+ canManageMcpSetup?: boolean;
561
+ onManage?: () => void;
457
562
  onConfigure: () => void;
563
+ onLegacyConfigure?: () => void;
458
564
  }) {
459
565
  const t = useT();
460
566
  const { disconnect } = useIntegration(def.id);
567
+ const showSettings =
568
+ def.id !== "gong" || canManageMcpSetup || legacyConnected;
461
569
 
462
570
  return (
463
571
  <div className="flex items-center gap-2.5 py-1.5 group relative">
@@ -475,32 +583,70 @@ function IntegrationRow({
475
583
  <div className="h-5 w-5 rounded-full bg-emerald-500/20 flex items-center justify-center">
476
584
  <IconCheck className="h-3 w-3 text-emerald-400" />
477
585
  </div>
478
- <DropdownMenu>
479
- <Tooltip>
480
- <TooltipTrigger asChild>
481
- <DropdownMenuTrigger asChild>
482
- <button className="h-5 w-5 flex items-center justify-center rounded text-muted-foreground/30 hover:text-muted-foreground transition-colors">
483
- <IconSettings className="h-3.5 w-3.5" />
484
- </button>
485
- </DropdownMenuTrigger>
486
- </TooltipTrigger>
487
- <TooltipContent>{t("mail.toolbar.settings")}</TooltipContent>
488
- </Tooltip>
489
- <DropdownMenuContent align="end" className="w-36">
490
- <DropdownMenuItem
491
- onClick={() => onConfigure()}
492
- className="text-[12px] text-foreground/70"
493
- >
494
- {t("mail.integrations.updateKey")}
495
- </DropdownMenuItem>
496
- <DropdownMenuItem
497
- onClick={() => disconnect.mutate()}
498
- className="text-[12px] text-red-400/80"
499
- >
500
- {t("mail.integrations.disconnect")}
501
- </DropdownMenuItem>
502
- </DropdownMenuContent>
503
- </DropdownMenu>
586
+ {showSettings && (
587
+ <DropdownMenu>
588
+ <Tooltip>
589
+ <TooltipTrigger asChild>
590
+ <DropdownMenuTrigger asChild>
591
+ <button
592
+ aria-label={t("mail.toolbar.settings")}
593
+ className="h-5 w-5 flex items-center justify-center rounded text-muted-foreground/30 hover:text-muted-foreground transition-colors"
594
+ >
595
+ <IconSettings className="h-3.5 w-3.5" />
596
+ </button>
597
+ </DropdownMenuTrigger>
598
+ </TooltipTrigger>
599
+ <TooltipContent>{t("mail.toolbar.settings")}</TooltipContent>
600
+ </Tooltip>
601
+ <DropdownMenuContent align="end" className="w-36">
602
+ {def.id === "gong" ? (
603
+ <>
604
+ {canManageMcpSetup && (
605
+ <DropdownMenuItem
606
+ onClick={mcpConnected ? onManage : onConfigure}
607
+ className="text-[12px] text-foreground/70"
608
+ >
609
+ {mcpConnected
610
+ ? t("mail.toolbar.settings")
611
+ : t("mail.integrations.connect")}
612
+ </DropdownMenuItem>
613
+ )}
614
+ {legacyConnected && (
615
+ <>
616
+ <DropdownMenuItem
617
+ onClick={onLegacyConfigure}
618
+ className="text-[12px] text-foreground/70"
619
+ >
620
+ {t("mail.integrations.updateKey")}
621
+ </DropdownMenuItem>
622
+ <DropdownMenuItem
623
+ onClick={() => disconnect.mutate()}
624
+ className="text-[12px] text-destructive/80"
625
+ >
626
+ {t("mail.integrations.disconnect")}
627
+ </DropdownMenuItem>
628
+ </>
629
+ )}
630
+ </>
631
+ ) : (
632
+ <>
633
+ <DropdownMenuItem
634
+ onClick={() => onConfigure()}
635
+ className="text-[12px] text-foreground/70"
636
+ >
637
+ {t("mail.integrations.updateKey")}
638
+ </DropdownMenuItem>
639
+ <DropdownMenuItem
640
+ onClick={() => disconnect.mutate()}
641
+ className="text-[12px] text-destructive/80"
642
+ >
643
+ {t("mail.integrations.disconnect")}
644
+ </DropdownMenuItem>
645
+ </>
646
+ )}
647
+ </DropdownMenuContent>
648
+ </DropdownMenu>
649
+ )}
504
650
  </div>
505
651
  ) : (
506
652
  <button
@@ -552,7 +698,11 @@ function IntegrationKeyEntry({
552
698
  setApiKey(e.target.value);
553
699
  if (connect.error) connect.reset();
554
700
  }}
555
- placeholder={def.keyPlaceholder}
701
+ placeholder={
702
+ def.keyPlaceholderKey
703
+ ? t(def.keyPlaceholderKey)
704
+ : def.keyPlaceholder
705
+ }
556
706
  autoFocus
557
707
  className="flex-1 min-w-0 rounded-md border border-border bg-background px-2 py-1 text-[12px] outline-none focus:border-primary/50 placeholder:text-muted-foreground/40"
558
708
  />
@@ -581,9 +731,12 @@ function IntegrationKeyEntry({
581
731
  {t("mail.integrations.getApiKey")}
582
732
  </p>
583
733
  <ol className="text-[11px] text-muted-foreground/50 space-y-0.5 list-decimal ps-3 mb-1.5">
584
- {def.helpSteps.map((step, i) => (
734
+ {def.helpSteps?.map((step, i) => (
585
735
  <li key={i}>{step}</li>
586
736
  ))}
737
+ {def.helpStepKeys?.map((key) => (
738
+ <li key={key}>{t(key)}</li>
739
+ ))}
587
740
  </ol>
588
741
  <a
589
742
  href={def.helpUrl}
@@ -610,6 +763,7 @@ function IntegrationNotice({
610
763
  error: unknown;
611
764
  providerId: ProviderId;
612
765
  }) {
766
+ const t = useT();
613
767
  const authErr = isAuthError(error);
614
768
  const [reconnecting, setReconnecting] = useState(false);
615
769
  const def = INTEGRATIONS.find((i) => i.id === providerId)!;
@@ -641,7 +795,11 @@ function IntegrationNotice({
641
795
  setApiKey(e.target.value);
642
796
  if (connect.error) connect.reset();
643
797
  }}
644
- placeholder={def.keyPlaceholder}
798
+ placeholder={
799
+ def.keyPlaceholderKey
800
+ ? t(def.keyPlaceholderKey)
801
+ : def.keyPlaceholder
802
+ }
645
803
  autoFocus
646
804
  className="flex-1 min-w-0 rounded-md border border-border bg-background px-2 py-1 text-[12px] outline-none focus:border-primary/50 placeholder:text-muted-foreground/40"
647
805
  />
@@ -1037,11 +1195,10 @@ function GongSection({ email }: { email: string }) {
1037
1195
 
1038
1196
  if (isLoading) return <SectionLoading />;
1039
1197
  if (!calls || calls.length === 0) {
1040
- if (error) {
1198
+ if (error)
1041
1199
  return (
1042
1200
  <IntegrationNotice email={email} error={error} providerId="gong" />
1043
1201
  );
1044
- }
1045
1202
  return null;
1046
1203
  }
1047
1204
 
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  callActionWithRetry,
3
+ currentActionQueryCacheBinding,
4
+ recordActionQueryBrowserPersist,
3
5
  useActionMutation,
4
6
  } from "@agent-native/core/client/hooks";
5
7
  import { agentNativeApiDisabledReason } from "@agent-native/core/client/host";
@@ -243,7 +245,20 @@ export function keepInboxProgressOrder(
243
245
  return { ...incoming, items };
244
246
  }
245
247
 
246
- let nextInboxSnapshotId = 0;
248
+ // Pages restored from the persisted query cache keep the ids of the page load
249
+ // that fetched them, so ids start from the clock and any fetch in this load
250
+ // outranks a restored page instead of losing to it.
251
+ const PAGE_LOAD_INBOX_SNAPSHOT_ID = Date.now();
252
+ let nextInboxSnapshotId = PAGE_LOAD_INBOX_SNAPSHOT_ID;
253
+
254
+ /** A restored page paints at once but is refetched on mount; this load's pages stay fresh. */
255
+ export function inboxThreadsStaleTime(query: {
256
+ state: { data?: { clientSnapshotId?: number } };
257
+ }): number {
258
+ return (query.state.data?.clientSnapshotId ?? 0) > PAGE_LOAD_INBOX_SNAPSHOT_ID
259
+ ? INBOX_THREADS_STALE_TIME_MS
260
+ : 0;
261
+ }
247
262
 
248
263
  function readInboxQueryParams(key: QueryKey): Record<string, unknown> | null {
249
264
  const params = key[2];
@@ -297,6 +312,9 @@ function fetchInboxThreads(
297
312
  queryKey: QueryKey,
298
313
  ): Promise<InboxQueryResult> {
299
314
  const clientSnapshotId = ++nextInboxSnapshotId;
315
+ const query = qc.getQueryCache().find({ queryKey, exact: true });
316
+ const cacheBinding = currentActionQueryCacheBinding();
317
+ let response: Response | undefined;
300
318
  return callActionWithRetry<ListInboxThreadsResult>(
301
319
  "list-inbox-threads",
302
320
  input,
@@ -304,8 +322,14 @@ function fetchInboxThreads(
304
322
  method: "GET",
305
323
  signal,
306
324
  timeoutMs: INBOX_THREADS_REQUEST_TIMEOUT_MS,
325
+ onResponse: (next) => {
326
+ response = next;
327
+ },
307
328
  },
308
329
  ).then((data) => {
330
+ if (query && !signal.aborted) {
331
+ recordActionQueryBrowserPersist(query, response, cacheBinding);
332
+ }
309
333
  const incoming = { ...data, clientSnapshotId };
310
334
  publishInboxOverview(qc, input.accountEmails, {
311
335
  tabs: incoming.tabs,
@@ -361,7 +385,7 @@ export function useInboxThreads(
361
385
  enabled: (opts?.enabled ?? true) && !agentNativeApiDisabledReason(),
362
386
  retry: false,
363
387
  refetchInterval: inboxThreadsRefetchInterval,
364
- staleTime: INBOX_THREADS_STALE_TIME_MS,
388
+ staleTime: inboxThreadsStaleTime,
365
389
  select: (data) => applyInboxMutationOverlay(qc, data) as InboxQueryResult,
366
390
  });
367
391
  }
@@ -305,7 +305,11 @@ const messages = {
305
305
  openProviderSettings: "Open {{provider}} Settings",
306
306
  apolloDescription: "Contact enrichment & company data",
307
307
  hubspotDescription: "CRM contacts, deals & tickets",
308
- gongDescription: "Recent calls & conversation intelligence",
308
+ gongDescription: "Gong call history",
309
+ gongAccessCheckFailed: "Couldn't check whether you can connect to Gong.",
310
+ gongKeyPlaceholder: "Gong API key",
311
+ gongKeyFormatHint:
312
+ "Enter the access key and secret as access_key:secret.",
309
313
  pylonDescription: "Support issues & account data",
310
314
  gongCalls: "Gong Calls",
311
315
  deals: "Deals",
@@ -0,0 +1,27 @@
1
+ import type { McpServersList } from "@agent-native/core/client/resources";
2
+
3
+ export function canManageGongConnection(
4
+ servers: McpServersList | undefined,
5
+ ): boolean {
6
+ if (!servers) return false;
7
+ if (servers.orgId === null) return true;
8
+ return servers.role === "owner" || servers.role === "admin";
9
+ }
10
+
11
+ export function hasConnectedGongMcpServer(
12
+ servers: McpServersList | undefined,
13
+ ): boolean {
14
+ if (!servers) return false;
15
+ return [...servers.user, ...servers.org].some(
16
+ (server) => server.status.state === "connected" && isGongMcpUrl(server.url),
17
+ );
18
+ }
19
+
20
+ function isGongMcpUrl(value: string): boolean {
21
+ if (!URL.canParse(value)) return false;
22
+ const url = new URL(value);
23
+ return (
24
+ url.origin === "https://mcp.gong.io" &&
25
+ url.pathname.replace(/\/+$/, "") === "/mcp"
26
+ );
27
+ }