@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
- package/corpus/templates/analytics/CHANGELOG.md +18 -0
- package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
- package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
- package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
- package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
- package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
- package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
- package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
- package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
- package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
- package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
- package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
- package/corpus/templates/analytics/server/db/schema.ts +1 -0
- package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
- package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
- package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
- package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
- package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
- package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
- package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
- package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
- package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
- package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
- package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
- package/corpus/templates/analytics/server/plugins/db.ts +9 -1
- package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
- package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
- package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/actions/delete-event.ts +1 -3
- package/corpus/templates/calendar/actions/delete-events.ts +1 -2
- package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
- package/corpus/templates/calendar/actions/update-event.ts +2 -6
- package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/CHANGELOG.md +11 -24
- package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
- package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
- package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
- package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
- package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
- package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
- package/corpus/templates/clips/app/root.tsx +22 -10
- package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
- package/corpus/templates/clips/app/routes/import.tsx +115 -110
- package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +29 -186
- package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
- package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
- package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
- package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
- package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
- package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/content/CHANGELOG.md +12 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
- package/corpus/templates/content/actions/_database-navigation.ts +3 -3
- package/corpus/templates/content/actions/_database-utils.ts +10 -22
- package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
- package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
- package/corpus/templates/content/actions/export-document.ts +2 -14
- package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
- package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
- package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
- package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
- package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
- package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
- package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
- package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
- package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
- package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
- package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
- package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
- package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
- package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
- package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
- package/corpus/templates/content/app/global.css +23 -0
- package/corpus/templates/content/app/i18n-data.ts +1 -0
- package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
- package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
- 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
- package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
- package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
- package/corpus/templates/content/e2e/helpers.ts +10 -0
- package/corpus/templates/content/shared/api.ts +18 -12
- package/corpus/templates/content/shared/database-query.ts +107 -13
- package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
- package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/CHANGELOG.md +2 -0
- package/corpus/templates/design/actions/delete-file.ts +1 -4
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
- 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
- package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +185 -85
- package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
- package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
- package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
- package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
- package/corpus/templates/mail/AGENTS.md +1 -1
- package/corpus/templates/mail/CHANGELOG.md +14 -0
- package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
- package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
- package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
- package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
- package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
- package/corpus/templates/mail/app/root.tsx +10 -2
- package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +9 -0
- package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/plan/CHANGELOG.md +4 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
- package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
- package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
- package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
- package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
- package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
- package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
- 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 {
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 !
|
|
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 =
|
|
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 {
|
|
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(() =>
|
|
583
|
+
const [queryClient] = useState(() => {
|
|
584
|
+
const client = createAgentNativeQueryClient();
|
|
585
|
+
bindThreadCacheQueryClient(client);
|
|
586
|
+
return client;
|
|
587
|
+
});
|
|
580
588
|
return (
|
|
581
589
|
<AppToolkitProvider>
|
|
582
590
|
<AppProviders
|
|
@@ -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
|
|
99
|
-
|
|
100
|
-
|
|
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.
|