@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,10 @@
1
1
  import { appApiPath } from "@agent-native/core/client/api-path";
2
+ import {
3
+ currentActionQueryCacheBinding,
4
+ recordActionQueryBrowserPersist,
5
+ } from "@agent-native/core/client/hooks";
2
6
  import type { EmailMessage } from "@shared/types";
7
+ import type { QueryClient } from "@tanstack/react-query";
3
8
  import { useEffect, useState } from "react";
4
9
 
5
10
  import { beginProviderSnapshot } from "@/lib/provider-snapshot";
@@ -14,6 +19,8 @@ type CacheEntry = {
14
19
  type ThreadFetchResult = {
15
20
  messages: EmailMessage[];
16
21
  providerSnapshotId: number;
22
+ response?: Response;
23
+ cacheBinding?: object;
17
24
  };
18
25
 
19
26
  type WarmTarget = string | { id: string; accountEmail?: string };
@@ -21,6 +28,12 @@ type WarmTarget = string | { id: string; accountEmail?: string };
21
28
  const BACKGROUND_RATE_LIMIT_COOLDOWN_MS = 90 * 1000;
22
29
  const BACKGROUND_AUTH_FAILURE_COOLDOWN_MS = 5 * 60 * 1000;
23
30
  const WARM_BATCH_LIMIT = 4;
31
+ // Recently fetched threads are mirrored into the action query cache, whose
32
+ // per-user persisted record (cleared on sign-out) paints them after a restart.
33
+ // A restored thread is only a placeholder until its refetch lands.
34
+ const PERSISTED_THREADS_KEY = ["action", "mail-thread-messages"] as const;
35
+ const PERSISTED_THREAD_LIMIT = 20;
36
+ let persistClient: QueryClient | undefined;
24
37
 
25
38
  type Globals = {
26
39
  __mailThreadCacheFormat?: number;
@@ -85,6 +98,83 @@ function keysForThread(threadId: string): string[] {
85
98
  ).filter((key) => threadIdFromCacheKey(key) === threadId);
86
99
  }
87
100
 
101
+ /** Call before the persisted query cache restores, so restored threads keep `gcTime: Infinity`. */
102
+ export function bindThreadCacheQueryClient(client: QueryClient): void {
103
+ persistClient = client;
104
+ client.setQueryDefaults([...PERSISTED_THREADS_KEY], { gcTime: Infinity });
105
+ }
106
+
107
+ function persistedThreadQueryKey(key: string) {
108
+ return [
109
+ ...PERSISTED_THREADS_KEY,
110
+ sessionScope ?? null,
111
+ accountScope ?? null,
112
+ key,
113
+ ];
114
+ }
115
+
116
+ function persistThread(
117
+ key: string,
118
+ entry: CacheEntry,
119
+ result: ThreadFetchResult,
120
+ ) {
121
+ if (!persistClient || sessionScope === undefined) return;
122
+ const queryCache = persistClient.getQueryCache();
123
+ const queryKey = persistedThreadQueryKey(key);
124
+ persistClient.setQueryData(queryKey, entry.messages, {
125
+ updatedAt: entry.fetchedAt,
126
+ });
127
+ const query = queryCache.find({ queryKey, exact: true });
128
+ if (query) {
129
+ recordActionQueryBrowserPersist(
130
+ query,
131
+ result.response,
132
+ result.cacheBinding,
133
+ );
134
+ }
135
+ const persisted = queryCache
136
+ .findAll({ queryKey: [...PERSISTED_THREADS_KEY] })
137
+ .sort((a, b) => b.state.dataUpdatedAt - a.state.dataUpdatedAt);
138
+ for (const evicted of persisted.slice(PERSISTED_THREAD_LIMIT)) {
139
+ queryCache.remove(evicted);
140
+ }
141
+ }
142
+
143
+ function forgetPersistedThreadsForThread(threadId: string) {
144
+ if (!persistClient) return;
145
+ const queryCache = persistClient.getQueryCache();
146
+ const queryKeyIndex = PERSISTED_THREADS_KEY.length + 2;
147
+ for (const query of queryCache.findAll({
148
+ queryKey: [...PERSISTED_THREADS_KEY],
149
+ })) {
150
+ const key = query.queryKey[queryKeyIndex];
151
+ if (typeof key !== "string" || threadIdFromCacheKey(key) !== threadId) {
152
+ continue;
153
+ }
154
+ persistClient.removeQueries({ queryKey: query.queryKey, exact: true });
155
+ }
156
+ }
157
+
158
+ function cachedEntry(key: string): CacheEntry | undefined {
159
+ const hit = cache.get(key);
160
+ if (hit || !persistClient || sessionScope === undefined) return hit;
161
+ const query = persistClient
162
+ .getQueryCache()
163
+ .find({ queryKey: persistedThreadQueryKey(key), exact: true });
164
+ const messages = query?.state.data;
165
+ if (!query || !Array.isArray(messages)) return undefined;
166
+ const restored = {
167
+ messages: messages as EmailMessage[],
168
+ fetchedAt: query.state.dataUpdatedAt,
169
+ };
170
+ cache.set(key, restored);
171
+ const parsed = JSON.parse(key) as [unknown, string | null, string];
172
+ if (canRunBackgroundFetch() && !inflight.has(key)) {
173
+ void backgroundRefresh(parsed[2], parsed[1] ?? undefined);
174
+ }
175
+ return restored;
176
+ }
177
+
88
178
  export function getThreadCacheOwner(): string | null {
89
179
  return g.__mailThreadOwner ?? null;
90
180
  }
@@ -200,6 +290,7 @@ async function fetchThread(
200
290
  accountEmail?: string,
201
291
  ): Promise<ThreadFetchResult> {
202
292
  const providerSnapshotId = beginProviderSnapshot();
293
+ const cacheBinding = currentActionQueryCacheBinding();
203
294
  const params = new URLSearchParams();
204
295
  if (accountEmail) params.set("accountEmail", accountEmail);
205
296
  const suffix = params.toString() ? `?${params}` : "";
@@ -231,7 +322,12 @@ async function fetchThread(
231
322
  retryAfterMs;
232
323
  throw error;
233
324
  }
234
- return { messages: await res.json(), providerSnapshotId };
325
+ return {
326
+ messages: await res.json(),
327
+ providerSnapshotId,
328
+ response: res,
329
+ cacheBinding,
330
+ };
235
331
  }
236
332
 
237
333
  function clearLegacyThreadStorage() {
@@ -247,7 +343,7 @@ export function getCachedThread(
247
343
  threadId: string,
248
344
  accountEmail?: string,
249
345
  ): EmailMessage[] | undefined {
250
- return cache.get(cacheKey(threadId, accountEmail))?.messages;
346
+ return cachedEntry(cacheKey(threadId, accountEmail))?.messages;
251
347
  }
252
348
 
253
349
  export function setCachedThread(
@@ -292,6 +388,7 @@ export function invalidateCachedThread(threadId: string) {
292
388
  versions.set(key, getVersion(key) + 1);
293
389
  notify(key);
294
390
  }
391
+ forgetPersistedThreadsForThread(threadId);
295
392
  }
296
393
 
297
394
  const STALE_AFTER = 60 * 1000;
@@ -301,7 +398,7 @@ export function ensureThread(
301
398
  accountEmail?: string,
302
399
  ): Promise<EmailMessage[]> {
303
400
  const key = cacheKey(threadId, accountEmail);
304
- const cached = cache.get(key);
401
+ const cached = cachedEntry(key);
305
402
  if (cached) {
306
403
  if (
307
404
  Date.now() - cached.fetchedAt > STALE_AFTER &&
@@ -321,11 +418,13 @@ export function ensureThread(
321
418
  clearOwnedInflight(key, p);
322
419
  return result;
323
420
  }
324
- cache.set(key, {
421
+ const entry = {
325
422
  messages: result.messages,
326
423
  fetchedAt: Date.now(),
327
424
  providerSnapshotId: result.providerSnapshotId,
328
- });
425
+ };
426
+ cache.set(key, entry);
427
+ persistThread(key, entry, result);
329
428
  clearOwnedInflight(key, p);
330
429
  notify(key);
331
430
  return result;
@@ -356,11 +455,13 @@ function backgroundRefresh(
356
455
  return result;
357
456
  }
358
457
  const prev = cache.get(key);
359
- cache.set(key, {
458
+ const entry = {
360
459
  messages: result.messages,
361
460
  fetchedAt: Date.now(),
362
461
  providerSnapshotId: result.providerSnapshotId,
363
- });
462
+ };
463
+ cache.set(key, entry);
464
+ persistThread(key, entry, result);
364
465
  clearOwnedInflight(key, p);
365
466
  const prevJson = prev ? JSON.stringify(prev.messages) : "";
366
467
  const nextJson = JSON.stringify(result.messages);
@@ -389,7 +490,7 @@ export function warmThreads(targets: WarmTarget[], concurrency = 2) {
389
490
  .map(normalizeTarget)
390
491
  .filter((target) => {
391
492
  const key = cacheKey(target.id, target.accountEmail);
392
- return !cache.has(key) && !inflight.has(key);
493
+ return !cachedEntry(key) && !inflight.has(key);
393
494
  })
394
495
  .slice(0, WARM_BATCH_LIMIT);
395
496
  if (queue.length === 0) return;
@@ -448,7 +549,7 @@ export function useThreadCache(
448
549
  isLoading: false,
449
550
  };
450
551
  }
451
- const hit = cache.get(key!);
552
+ const hit = cachedEntry(key!);
452
553
  if (hit) {
453
554
  return {
454
555
  messages: hit.messages,
@@ -49,7 +49,11 @@ import {
49
49
  } from "@/lib/integration-status";
50
50
  import { shouldInvalidateMailQueryForActionEvent } from "@/lib/sync-invalidation";
51
51
  import { TAB_ID } from "@/lib/tab-id";
52
- import { getThreadCacheOwner, setThreadCacheOwner } from "@/lib/thread-cache";
52
+ import {
53
+ bindThreadCacheQueryClient,
54
+ getThreadCacheOwner,
55
+ setThreadCacheOwner,
56
+ } from "@/lib/thread-cache";
53
57
 
54
58
  import { i18nCatalog } from "./i18n";
55
59
 
@@ -576,7 +580,11 @@ export function computeSessionBypass(): boolean {
576
580
  }
577
581
 
578
582
  export default function Root() {
579
- const [queryClient] = useState(() => createAgentNativeQueryClient());
583
+ const [queryClient] = useState(() => {
584
+ const client = createAgentNativeQueryClient();
585
+ bindThreadCacheQueryClient(client);
586
+ return client;
587
+ });
580
588
  return (
581
589
  <AppToolkitProvider>
582
590
  <AppProviders
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Connected Gong settings now open the shared integration manager.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Gong connection details now match the call data available in Mail.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Improve Gong setup with a click-to-connect flow and permission gating
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Mail lets you retry when it cannot check your Gong access.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Cached conversations stay with the mailbox they belong to.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ The inbox and recently opened threads now open instantly from a cache on your device, cleared when you sign out, then refresh in the background.
@@ -7,6 +7,10 @@ import {
7
7
  import { readBody, getSession } from "@agent-native/core/server";
8
8
  import { getAppProductionUrl } from "@agent-native/core/server";
9
9
  import { getUserSetting, putUserSetting } from "@agent-native/core/settings";
10
+ import {
11
+ ACTION_BROWSER_PERSIST_ALLOW,
12
+ ACTION_BROWSER_PERSIST_HEADER,
13
+ } from "@agent-native/core/shared";
10
14
  import {
11
15
  gmailReadState,
12
16
  GMAIL_QUOTA_COOLDOWN_ERROR_CODE,
@@ -800,6 +804,11 @@ export const listEmails = defineEventHandler(async (event: H3Event) => {
800
804
  // ─── Thread messages ─────────────────────────────────────────────────────────
801
805
 
802
806
  export const getThreadMessages = defineEventHandler(async (event: H3Event) => {
807
+ setResponseHeader(
808
+ event,
809
+ ACTION_BROWSER_PERSIST_HEADER,
810
+ ACTION_BROWSER_PERSIST_ALLOW,
811
+ );
803
812
  const email = await userEmail(event);
804
813
  const threadId = getRouterParam(event, "threadId") as string;
805
814
  const { accountEmail } = getQuery(event) as { accountEmail?: string };
@@ -95,9 +95,9 @@ MCP clients see no `outputSchema` by default. Set `mcpOutputSchema: true` (requi
95
95
 
96
96
  `authorize` decides whether the caller may run the action at all. It wraps
97
97
  `run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
98
- hook, MCP, A2A, CLI — unlike `needsApproval`, which is honoured only in the
99
- agent loop. Use `authorize` for "not everyone may do this"; use `needsApproval`
100
- to ask a human to bless one call a permitted caller is already allowed to make.
98
+ hook, MCP, A2A, CLI. Use `authorize` for "not everyone may do this"; use
99
+ `needsApproval` to ask a human to bless one call a permitted caller is already
100
+ allowed to make.
101
101
 
102
102
  ```ts
103
103
  import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
@@ -140,4 +140,6 @@ export default defineAction({
140
140
 
141
141
  When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". It receives its own copy of the prepared snapshot of the validated arguments, defaults applied, and the approval binds to the snapshot's hash, so an approved call runs only with the arguments that were approved (a Date binds by its time). The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run.
142
142
 
143
+ Approval is given in the app, so HTTP never grants it. The HTTP action route and the generated Workers entry run a gated call only for the proven app UI (a frontend call from the signed-in user, same origin, holding the short-lived UI capability; `isProvenUiRequest`, the proof `uiOnly` uses) or the app's MCP directory widget. Every other HTTP caller (bearer or API key, MCP OAuth token, org service token, A2A, extension iframe, mobile app) gets `409 approval_required`, and a service identity can never approve. The shared client presents the capability with every write from a signed-in session (minted once per user, reused for nine minutes, never for reads or public pages), so a gated button works on its first request. A call without proof gets `403 ui_capability_required`; the client then re-mints and retries once, which is only the fallback for an expired or rejected capability. Every surface evaluates the gate through `preparedCallNeedsApproval`, so a throwing predicate asks there too. A caller that must run unattended needs a different action, never a bypass of the gate.
144
+
143
145
  **Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc.
@@ -5,6 +5,10 @@ time from the command menu (Cmd+K → "What's new").
5
5
 
6
6
  ## 2026-10-19
7
7
 
8
+ ### Fixed
9
+
10
+ - Opening a comment thread puts the cursor in the reply box, and Edit puts it after the comment's text. A reply you haven't sent stays with its thread when you open another one.
11
+
8
12
  ### Changed
9
13
 
10
14
  - Apps no longer keep server functions and databases warm by default; set `AGENT_NATIVE_ENABLE_KEEP_WARM=1` to opt in.