@agent-native/core 0.77.16 → 0.77.17
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/core/CHANGELOG.md +8 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +4 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +4 -0
- package/corpus/core/src/client/use-db-sync.ts +19 -2
- package/corpus/core/src/server/auth-marketing.ts +15 -0
- package/corpus/core/src/server/onboarding-html.ts +47 -1
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +76 -0
- package/corpus/templates/analytics/actions/update-dashboard.ts +1 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +13 -2
- package/corpus/templates/analytics/app/i18n-data.ts +10 -0
- package/corpus/templates/analytics/app/lib/sql-query.ts +97 -21
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +22 -1
- package/corpus/templates/analytics/app/root.tsx +15 -6
- package/corpus/templates/analytics/changelog/2026-06-25-dashboard-charts-no-longer-refresh-in-the-background-during-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-25-first-party-dashboards-use-indexed-event-dates-for-faster-da.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-25-retention-and-active-user-dashboard-panels-now-count-account.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +58 -58
- package/corpus/templates/analytics/server/db/schema.ts +2 -0
- package/corpus/templates/analytics/server/handlers/sql-query.ts +1 -1
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +16 -6
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +88 -56
- package/corpus/templates/analytics/server/plugins/db.ts +73 -0
- package/corpus/templates/clips/AGENTS.md +5 -0
- package/corpus/templates/clips/changelog/2026-06-25-github-issue-and-pull-request-pages-can-now-preview-playable.md +6 -0
- package/corpus/templates/clips/chrome-extension/PERMISSIONS.md +8 -2
- package/corpus/templates/clips/chrome-extension/public/manifest.json +14 -2
- package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +233 -0
- package/corpus/templates/clips/chrome-extension/src/github-preview.html +12 -0
- package/corpus/templates/clips/chrome-extension/src/github-preview.ts +414 -0
- package/corpus/templates/clips/chrome-extension/vite.config.ts +5 -0
- package/corpus/templates/mail/actions/archive-email.ts +13 -3
- package/corpus/templates/mail/app/hooks/use-emails.ts +5 -2
- package/corpus/templates/mail/changelog/2026-06-25-archive-failures-now-explain-when-gmail-needs-reconnecting-p.md +6 -0
- package/corpus/templates/mail/shared/archive-errors.ts +137 -0
- package/corpus/templates/plan/changelog/2026-06-25-hosted-signup-now-shows-how-to-switch-visual-plan-to-local-files.md +6 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +3 -1
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +9 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +8 -1
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/server/auth-marketing.d.ts +4 -0
- package/dist/server/auth-marketing.d.ts.map +1 -1
- package/dist/server/auth-marketing.js +9 -0
- package/dist/server/auth-marketing.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +46 -1
- package/dist/server/onboarding-html.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import { captureExtensionError, initExtensionSentry } from "./sentry";
|
|
2
|
+
|
|
3
|
+
initExtensionSentry("github-preview");
|
|
4
|
+
|
|
5
|
+
const DEFAULT_HOST = "clips.agent-native.com";
|
|
6
|
+
const CLIPS_HOSTS = new Set(["clips.agent-native.com"]);
|
|
7
|
+
const LOCAL_HOSTS = new Set(["localhost", "127.0.0.1"]);
|
|
8
|
+
|
|
9
|
+
type PreviewParams = {
|
|
10
|
+
frameId: string;
|
|
11
|
+
embedUrl: URL;
|
|
12
|
+
metadataUrl: URL;
|
|
13
|
+
sourceUrl: URL;
|
|
14
|
+
clipId: string;
|
|
15
|
+
host: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type PublicRecordingResponse = {
|
|
19
|
+
recording?: {
|
|
20
|
+
title?: string | null;
|
|
21
|
+
status?: string | null;
|
|
22
|
+
videoUrl?: string | null;
|
|
23
|
+
visibility?: string | null;
|
|
24
|
+
hasPassword?: boolean | null;
|
|
25
|
+
} | null;
|
|
26
|
+
error?: string;
|
|
27
|
+
passwordRequired?: boolean;
|
|
28
|
+
expired?: boolean;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type PreviewState =
|
|
32
|
+
| { kind: "checking" }
|
|
33
|
+
| { kind: "playable"; title: string }
|
|
34
|
+
| { kind: "unavailable"; title: string; detail: string };
|
|
35
|
+
|
|
36
|
+
function parseUrl(value: string | null): URL | null {
|
|
37
|
+
if (!value) return null;
|
|
38
|
+
try {
|
|
39
|
+
const parsed = new URL(value);
|
|
40
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return parsed;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isAllowedPreviewOrigin(url: URL): boolean {
|
|
50
|
+
if (CLIPS_HOSTS.has(url.hostname)) return true;
|
|
51
|
+
if (LOCAL_HOSTS.has(url.hostname)) return true;
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function validPreviewParams(params: PreviewParams): boolean {
|
|
56
|
+
if (!isAllowedPreviewOrigin(params.embedUrl)) return false;
|
|
57
|
+
if (!isAllowedPreviewOrigin(params.metadataUrl)) return false;
|
|
58
|
+
if (!isAllowedPreviewOrigin(params.sourceUrl)) return false;
|
|
59
|
+
if (params.embedUrl.origin !== params.metadataUrl.origin) return false;
|
|
60
|
+
if (params.sourceUrl.origin !== params.metadataUrl.origin) return false;
|
|
61
|
+
return /(?:^|\/)api\/public-recording$/.test(params.metadataUrl.pathname);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function readParams(): PreviewParams | null {
|
|
65
|
+
const params = new URLSearchParams(location.search);
|
|
66
|
+
const frameId = params.get("frameId")?.trim();
|
|
67
|
+
const embedUrl = parseUrl(params.get("embedUrl"));
|
|
68
|
+
const metadataUrl = parseUrl(params.get("metadataUrl"));
|
|
69
|
+
const sourceUrl = parseUrl(params.get("sourceUrl"));
|
|
70
|
+
const clipId = params.get("clipId")?.trim() ?? "";
|
|
71
|
+
const host = params.get("host")?.trim() || DEFAULT_HOST;
|
|
72
|
+
if (!frameId || !embedUrl || !metadataUrl || !sourceUrl || !clipId) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const preview = { frameId, embedUrl, metadataUrl, sourceUrl, clipId, host };
|
|
76
|
+
return validPreviewParams(preview) ? preview : null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function postHeight(frameId: string): void {
|
|
80
|
+
const height = document.documentElement.scrollHeight;
|
|
81
|
+
window.parent.postMessage(
|
|
82
|
+
{
|
|
83
|
+
source: "clips-github-preview",
|
|
84
|
+
frameId,
|
|
85
|
+
height,
|
|
86
|
+
},
|
|
87
|
+
"*",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function installAutoResize(frameId: string): void {
|
|
92
|
+
const emit = () => postHeight(frameId);
|
|
93
|
+
try {
|
|
94
|
+
const observer = new ResizeObserver(emit);
|
|
95
|
+
observer.observe(document.documentElement);
|
|
96
|
+
observer.observe(document.body);
|
|
97
|
+
} catch {
|
|
98
|
+
window.addEventListener("resize", emit);
|
|
99
|
+
}
|
|
100
|
+
requestAnimationFrame(emit);
|
|
101
|
+
setTimeout(emit, 250);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function renderInvalid(root: HTMLElement): void {
|
|
105
|
+
root.textContent = "";
|
|
106
|
+
const card = document.createElement("section");
|
|
107
|
+
card.className = "clips-preview-card clips-preview-error";
|
|
108
|
+
const title = document.createElement("strong");
|
|
109
|
+
title.textContent = "Clip preview unavailable";
|
|
110
|
+
const body = document.createElement("p");
|
|
111
|
+
body.textContent = "This Clips link could not be parsed by the extension.";
|
|
112
|
+
card.append(title, body);
|
|
113
|
+
root.append(card);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function renderPreview(
|
|
117
|
+
root: HTMLElement,
|
|
118
|
+
preview: PreviewParams,
|
|
119
|
+
state: PreviewState,
|
|
120
|
+
): void {
|
|
121
|
+
root.textContent = "";
|
|
122
|
+
|
|
123
|
+
const card = document.createElement("section");
|
|
124
|
+
card.className = "clips-preview-card";
|
|
125
|
+
|
|
126
|
+
const header = document.createElement("header");
|
|
127
|
+
header.className = "clips-preview-header";
|
|
128
|
+
|
|
129
|
+
const titleWrap = document.createElement("div");
|
|
130
|
+
titleWrap.className = "clips-preview-title-wrap";
|
|
131
|
+
|
|
132
|
+
const title = document.createElement("div");
|
|
133
|
+
title.className = "clips-preview-title";
|
|
134
|
+
title.textContent =
|
|
135
|
+
state.kind === "playable" || state.kind === "unavailable"
|
|
136
|
+
? state.title
|
|
137
|
+
: "Clips preview";
|
|
138
|
+
|
|
139
|
+
const subtitle = document.createElement("div");
|
|
140
|
+
subtitle.className = "clips-preview-subtitle";
|
|
141
|
+
subtitle.textContent = preview.host;
|
|
142
|
+
|
|
143
|
+
titleWrap.append(title, subtitle);
|
|
144
|
+
|
|
145
|
+
const open = document.createElement("a");
|
|
146
|
+
open.className = "clips-preview-open";
|
|
147
|
+
open.href = preview.sourceUrl.toString();
|
|
148
|
+
open.target = "_blank";
|
|
149
|
+
open.rel = "noreferrer noopener";
|
|
150
|
+
open.textContent = "Open clip";
|
|
151
|
+
|
|
152
|
+
header.append(titleWrap, open);
|
|
153
|
+
card.append(header);
|
|
154
|
+
|
|
155
|
+
if (state.kind === "playable") {
|
|
156
|
+
const player = document.createElement("div");
|
|
157
|
+
player.className = "clips-preview-player";
|
|
158
|
+
|
|
159
|
+
const frame = document.createElement("iframe");
|
|
160
|
+
frame.title = "Playable Clips embed";
|
|
161
|
+
frame.src = preview.embedUrl.toString();
|
|
162
|
+
frame.allow = "autoplay; fullscreen; picture-in-picture";
|
|
163
|
+
frame.allowFullscreen = true;
|
|
164
|
+
frame.referrerPolicy = "no-referrer";
|
|
165
|
+
|
|
166
|
+
player.append(frame);
|
|
167
|
+
card.append(player);
|
|
168
|
+
} else {
|
|
169
|
+
const body = document.createElement("div");
|
|
170
|
+
body.className = "clips-preview-message";
|
|
171
|
+
body.textContent =
|
|
172
|
+
state.kind === "checking"
|
|
173
|
+
? "Checking whether this clip can play inline..."
|
|
174
|
+
: state.detail;
|
|
175
|
+
card.append(body);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
root.append(card);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function checkPublicPlayable(
|
|
182
|
+
preview: PreviewParams,
|
|
183
|
+
): Promise<PreviewState> {
|
|
184
|
+
try {
|
|
185
|
+
const response = await fetch(preview.metadataUrl.toString(), {
|
|
186
|
+
credentials: "omit",
|
|
187
|
+
headers: { Accept: "application/json" },
|
|
188
|
+
});
|
|
189
|
+
const data = (await response
|
|
190
|
+
.json()
|
|
191
|
+
.catch(() => ({}))) as PublicRecordingResponse;
|
|
192
|
+
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
if (response.status === 401 || data.passwordRequired) {
|
|
195
|
+
return {
|
|
196
|
+
kind: "unavailable",
|
|
197
|
+
title: "Protected Clips link",
|
|
198
|
+
detail: "Open this clip in Clips to enter the password.",
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (response.status === 410 || data.expired) {
|
|
202
|
+
return {
|
|
203
|
+
kind: "unavailable",
|
|
204
|
+
title: "Expired Clips link",
|
|
205
|
+
detail: "This clip has expired. Open it in Clips for details.",
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
kind: "unavailable",
|
|
210
|
+
title: "Clips link",
|
|
211
|
+
detail: "Open this clip in Clips to view it.",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const recording = data.recording;
|
|
216
|
+
const title = recording?.title?.trim() || "Clips preview";
|
|
217
|
+
if (
|
|
218
|
+
recording?.visibility !== "public" ||
|
|
219
|
+
recording.hasPassword ||
|
|
220
|
+
recording.status !== "ready" ||
|
|
221
|
+
!recording.videoUrl
|
|
222
|
+
) {
|
|
223
|
+
return {
|
|
224
|
+
kind: "unavailable",
|
|
225
|
+
title,
|
|
226
|
+
detail: "Open this clip in Clips to view it.",
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return { kind: "playable", title };
|
|
231
|
+
} catch {
|
|
232
|
+
return {
|
|
233
|
+
kind: "unavailable",
|
|
234
|
+
title: "Clips link",
|
|
235
|
+
detail: "Open this clip in Clips to view it.",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function installStyles(): void {
|
|
241
|
+
const style = document.createElement("style");
|
|
242
|
+
style.textContent = `
|
|
243
|
+
:root {
|
|
244
|
+
color-scheme: light dark;
|
|
245
|
+
--clips-bg: #ffffff;
|
|
246
|
+
--clips-fg: #24292f;
|
|
247
|
+
--clips-muted: #57606a;
|
|
248
|
+
--clips-border: #d0d7de;
|
|
249
|
+
--clips-button-bg: #f6f8fa;
|
|
250
|
+
--clips-button-hover: #eef1f4;
|
|
251
|
+
--clips-shadow: 0 8px 24px rgba(140, 149, 159, 0.2);
|
|
252
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
253
|
+
background: transparent;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
@media (prefers-color-scheme: dark) {
|
|
257
|
+
:root {
|
|
258
|
+
--clips-bg: #0d1117;
|
|
259
|
+
--clips-fg: #f0f6fc;
|
|
260
|
+
--clips-muted: #8b949e;
|
|
261
|
+
--clips-border: #30363d;
|
|
262
|
+
--clips-button-bg: #21262d;
|
|
263
|
+
--clips-button-hover: #30363d;
|
|
264
|
+
--clips-shadow: 0 8px 24px rgba(1, 4, 9, 0.38);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
* {
|
|
269
|
+
box-sizing: border-box;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
html,
|
|
273
|
+
body {
|
|
274
|
+
margin: 0;
|
|
275
|
+
min-width: 0;
|
|
276
|
+
background: transparent;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
body {
|
|
280
|
+
padding: 0;
|
|
281
|
+
overflow: hidden;
|
|
282
|
+
color: var(--clips-fg);
|
|
283
|
+
-webkit-font-smoothing: antialiased;
|
|
284
|
+
-moz-osx-font-smoothing: grayscale;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.clips-preview-card {
|
|
288
|
+
overflow: hidden;
|
|
289
|
+
border: 1px solid var(--clips-border);
|
|
290
|
+
border-radius: 8px;
|
|
291
|
+
background: var(--clips-bg);
|
|
292
|
+
box-shadow: var(--clips-shadow);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.clips-preview-header {
|
|
296
|
+
display: flex;
|
|
297
|
+
align-items: center;
|
|
298
|
+
gap: 12px;
|
|
299
|
+
min-height: 44px;
|
|
300
|
+
padding: 10px 12px;
|
|
301
|
+
border-bottom: 1px solid var(--clips-border);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.clips-preview-title-wrap {
|
|
305
|
+
min-width: 0;
|
|
306
|
+
flex: 1;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.clips-preview-title {
|
|
310
|
+
overflow: hidden;
|
|
311
|
+
text-overflow: ellipsis;
|
|
312
|
+
white-space: nowrap;
|
|
313
|
+
font-size: 13px;
|
|
314
|
+
font-weight: 600;
|
|
315
|
+
line-height: 18px;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.clips-preview-subtitle {
|
|
319
|
+
overflow: hidden;
|
|
320
|
+
text-overflow: ellipsis;
|
|
321
|
+
white-space: nowrap;
|
|
322
|
+
color: var(--clips-muted);
|
|
323
|
+
font-size: 12px;
|
|
324
|
+
line-height: 16px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.clips-preview-open {
|
|
328
|
+
flex: 0 0 auto;
|
|
329
|
+
border: 1px solid var(--clips-border);
|
|
330
|
+
border-radius: 6px;
|
|
331
|
+
background: var(--clips-button-bg);
|
|
332
|
+
color: var(--clips-fg);
|
|
333
|
+
padding: 5px 9px;
|
|
334
|
+
font-size: 12px;
|
|
335
|
+
font-weight: 600;
|
|
336
|
+
line-height: 18px;
|
|
337
|
+
text-decoration: none;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.clips-preview-open:hover,
|
|
341
|
+
.clips-preview-open:focus-visible {
|
|
342
|
+
background: var(--clips-button-hover);
|
|
343
|
+
text-decoration: none;
|
|
344
|
+
outline: none;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.clips-preview-player {
|
|
348
|
+
position: relative;
|
|
349
|
+
width: 100%;
|
|
350
|
+
aspect-ratio: 16 / 9;
|
|
351
|
+
min-height: 240px;
|
|
352
|
+
background: #000000;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.clips-preview-player iframe {
|
|
356
|
+
position: absolute;
|
|
357
|
+
inset: 0;
|
|
358
|
+
width: 100%;
|
|
359
|
+
height: 100%;
|
|
360
|
+
border: 0;
|
|
361
|
+
background: #000000;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.clips-preview-message {
|
|
365
|
+
padding: 12px;
|
|
366
|
+
color: var(--clips-muted);
|
|
367
|
+
font-size: 12px;
|
|
368
|
+
line-height: 18px;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.clips-preview-error {
|
|
372
|
+
padding: 12px;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.clips-preview-error strong {
|
|
376
|
+
display: block;
|
|
377
|
+
font-size: 13px;
|
|
378
|
+
line-height: 18px;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.clips-preview-error p {
|
|
382
|
+
margin: 4px 0 0;
|
|
383
|
+
color: var(--clips-muted);
|
|
384
|
+
font-size: 12px;
|
|
385
|
+
line-height: 18px;
|
|
386
|
+
}
|
|
387
|
+
`;
|
|
388
|
+
document.head.append(style);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async function main(): Promise<void> {
|
|
392
|
+
try {
|
|
393
|
+
installStyles();
|
|
394
|
+
const root = document.getElementById("root");
|
|
395
|
+
if (!root) throw new Error("Missing preview root");
|
|
396
|
+
|
|
397
|
+
const preview = readParams();
|
|
398
|
+
if (!preview) {
|
|
399
|
+
renderInvalid(root);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
renderPreview(root, preview, { kind: "checking" });
|
|
404
|
+
installAutoResize(preview.frameId);
|
|
405
|
+
renderPreview(root, preview, await checkPublicPlayable(preview));
|
|
406
|
+
} catch (error) {
|
|
407
|
+
captureExtensionError(error, {
|
|
408
|
+
tags: { surface: "github-preview" },
|
|
409
|
+
extra: { pageUrl: location.href },
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
main();
|
|
@@ -69,6 +69,11 @@ export default defineConfig({
|
|
|
69
69
|
input: {
|
|
70
70
|
background: resolve(root, "src/background.ts"),
|
|
71
71
|
"content-script": resolve(root, "src/content-script.ts"),
|
|
72
|
+
"github-preview-content": resolve(
|
|
73
|
+
root,
|
|
74
|
+
"src/github-preview-content.ts",
|
|
75
|
+
),
|
|
76
|
+
"github-preview": resolve(root, "src/github-preview.html"),
|
|
72
77
|
offscreen: resolve(root, "src/offscreen.html"),
|
|
73
78
|
overlay: resolve(root, "src/overlay.html"),
|
|
74
79
|
permission: resolve(root, "src/permission.html"),
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { writeAppState } from "@agent-native/core/application-state";
|
|
3
3
|
import { getRequestUserEmail } from "@agent-native/core/server";
|
|
4
|
+
import { summarizeArchiveFailures } from "@shared/archive-errors.js";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
|
|
6
7
|
import { archiveEmail } from "../server/lib/email-state.js";
|
|
7
8
|
|
|
9
|
+
function userFacingActionError(message: string, statusCode: number): Error {
|
|
10
|
+
const error = new Error(message) as Error & { statusCode: number };
|
|
11
|
+
error.statusCode = statusCode;
|
|
12
|
+
return error;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
export default defineAction({
|
|
9
16
|
description:
|
|
10
17
|
"Archive one or more emails by ID. The UI handles navigation to the next email automatically.",
|
|
@@ -61,9 +68,12 @@ export default defineAction({
|
|
|
61
68
|
const failed = results.filter((r) => !r.success);
|
|
62
69
|
|
|
63
70
|
if (failed.length > 0) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
const summary = summarizeArchiveFailures({
|
|
72
|
+
succeeded,
|
|
73
|
+
total: ids.length,
|
|
74
|
+
failures: failed.map((r) => r.error ?? "failed"),
|
|
75
|
+
});
|
|
76
|
+
throw userFacingActionError(summary.message, summary.statusCode);
|
|
67
77
|
}
|
|
68
78
|
return `Archived ${succeeded} email(s) successfully`;
|
|
69
79
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { appApiPath, callAction, useT } from "@agent-native/core/client";
|
|
2
|
+
import { archiveFailureToastMessage } from "@shared/archive-errors";
|
|
2
3
|
import { markdownPreviewSnippet } from "@shared/markdown";
|
|
3
4
|
import type {
|
|
4
5
|
ComposeAttachment,
|
|
@@ -809,10 +810,12 @@ export function useArchiveEmail() {
|
|
|
809
810
|
);
|
|
810
811
|
return { previous, threadId };
|
|
811
812
|
},
|
|
812
|
-
onError: (
|
|
813
|
+
onError: (err, _vars, context) => {
|
|
813
814
|
if (context?.threadId) unsuppressThread(context.threadId);
|
|
814
815
|
context?.previous.forEach(([key, data]) => qc.setQueryData(key, data));
|
|
815
|
-
toast.error(
|
|
816
|
+
toast.error(
|
|
817
|
+
archiveFailureToastMessage(err, t("mail.toasts.archiveFailed")),
|
|
818
|
+
);
|
|
816
819
|
},
|
|
817
820
|
onSettled: () => delayedInvalidate(qc, [["emails"], ["labels"]]),
|
|
818
821
|
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
export interface ArchiveFailureMessage {
|
|
2
|
+
message: string;
|
|
3
|
+
statusCode: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface ArchiveFailureDetail {
|
|
7
|
+
detail: string;
|
|
8
|
+
statusCode: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function errorText(error: unknown): string {
|
|
12
|
+
if (error instanceof Error) return error.message;
|
|
13
|
+
if (typeof error === "string") return error;
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function withoutActionPrefix(message: string): string {
|
|
18
|
+
return message.replace(/^Action\s+archive-email\s+failed:\s*/i, "").trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function quotaDetail(message: string): string {
|
|
22
|
+
const wait = message.match(/ready again in about ([^.]+)\./i)?.[1];
|
|
23
|
+
if (wait) {
|
|
24
|
+
return `Gmail is briefly busy and will be ready again in about ${wait}.`;
|
|
25
|
+
}
|
|
26
|
+
return "Gmail is briefly busy. Try again in a moment.";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function classifyArchiveFailure(message: string): ArchiveFailureDetail {
|
|
30
|
+
const lower = message.toLowerCase();
|
|
31
|
+
|
|
32
|
+
if (
|
|
33
|
+
/\b(429|quota|rate limit|ratelimit|user-rate)\b/i.test(message) ||
|
|
34
|
+
lower.includes("briefly busy")
|
|
35
|
+
) {
|
|
36
|
+
return { detail: quotaDetail(message), statusCode: 429 };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
lower.includes("invalid_grant") ||
|
|
41
|
+
lower.includes("invalid_client") ||
|
|
42
|
+
lower.includes("unauthorized_client") ||
|
|
43
|
+
lower.includes("oauth token refresh failed") ||
|
|
44
|
+
lower.includes("no valid access token") ||
|
|
45
|
+
/\b401\b/.test(lower)
|
|
46
|
+
) {
|
|
47
|
+
return {
|
|
48
|
+
detail: "Gmail needs to be reconnected.",
|
|
49
|
+
statusCode: 409,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
lower.includes("insufficient_scope") ||
|
|
55
|
+
lower.includes("insufficient permissions") ||
|
|
56
|
+
lower.includes("forbidden") ||
|
|
57
|
+
lower.includes("gmail.modify") ||
|
|
58
|
+
/\b403\b/.test(lower)
|
|
59
|
+
) {
|
|
60
|
+
return {
|
|
61
|
+
detail:
|
|
62
|
+
"this Google connection does not have permission to modify Gmail. Reconnect Gmail and grant Mail access.",
|
|
63
|
+
statusCode: 409,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (lower.includes("no google account connected")) {
|
|
68
|
+
return {
|
|
69
|
+
detail: "no Google account is connected. Connect Gmail before archiving.",
|
|
70
|
+
statusCode: 409,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (lower.includes("account not owned") || lower.includes("not connected")) {
|
|
75
|
+
return {
|
|
76
|
+
detail: "the Gmail account for this conversation is not connected.",
|
|
77
|
+
statusCode: 409,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (
|
|
82
|
+
lower.includes("not found") ||
|
|
83
|
+
lower.includes("thread not found") ||
|
|
84
|
+
/\b404\b/.test(lower)
|
|
85
|
+
) {
|
|
86
|
+
return {
|
|
87
|
+
detail:
|
|
88
|
+
"Gmail no longer has that conversation. Refresh Mail and try again.",
|
|
89
|
+
statusCode: 409,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
detail: "Gmail did not accept the change. Refresh Mail and try again.",
|
|
95
|
+
statusCode: 409,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function summarizeArchiveFailures(input: {
|
|
100
|
+
succeeded: number;
|
|
101
|
+
total: number;
|
|
102
|
+
failures: string[];
|
|
103
|
+
}): ArchiveFailureMessage {
|
|
104
|
+
const firstFailure = input.failures.find((failure) => failure.trim()) ?? "";
|
|
105
|
+
const classified = classifyArchiveFailure(firstFailure);
|
|
106
|
+
const failedCount = Math.max(0, input.total - input.succeeded);
|
|
107
|
+
|
|
108
|
+
if (input.total <= 1) {
|
|
109
|
+
return {
|
|
110
|
+
message: `Archive failed because ${classified.detail}`,
|
|
111
|
+
statusCode: classified.statusCode,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
message: `Archived ${input.succeeded}/${input.total} conversations. Could not archive ${failedCount} because ${classified.detail}`,
|
|
117
|
+
statusCode: classified.statusCode,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function archiveFailureToastMessage(
|
|
122
|
+
error: unknown,
|
|
123
|
+
fallback: string,
|
|
124
|
+
): string {
|
|
125
|
+
const message = withoutActionPrefix(errorText(error));
|
|
126
|
+
if (!message || /^internal server error$/i.test(message)) return fallback;
|
|
127
|
+
|
|
128
|
+
if (/\bFailures:/i.test(message)) {
|
|
129
|
+
return summarizeArchiveFailures({
|
|
130
|
+
succeeded: 0,
|
|
131
|
+
total: 1,
|
|
132
|
+
failures: [message],
|
|
133
|
+
}).message;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return message;
|
|
137
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentPanel.d.ts","sourceRoot":"","sources":["../../src/client/AgentPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAmBH,OAAO,KASN,MAAM,OAAO,CAAC;AA0Cf,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAW7D,OAAO,KAAK,EACV,gCAAgC,EAChC,0BAA0B,EAC3B,MAAM,4BAA4B,CAAC;AA4DpC,KAAK,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC;AAC3D,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,SAAS,EACf,iBAAiB,EAAE,OAAO,GACzB,SAAS,CAEX;AAwLD,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,gCAAgC,CAAC,MAAM,CAAC,EAC9C,WAAW,EAAE,MAAM;;;;;;EAcpB;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,gCAAgC,CAAC,MAAM,CAAC,EAC9C,WAAW,EAAE,MAAM,WAOpB;AAED,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,gCAAgC,CAAC,MAAM,CAAC,EAC/C,WAAW,EAAE,MAAM,EACnB,sBAAsB,EAAE,MAAM,WAG/B;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,EAAE,WAE9D;AAID,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,OAAO,EAAE,OAAO,CAAC;IACjB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,mEAAmE;IACnE,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,wEAAwE;IACxE,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACzC;AAgGD,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,kBAAkB,EAClB,eAAe,CAChB;IACC,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,6GAA6G;IAC7G,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,iFAAiF;IACjF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6HAA6H;IAC7H,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,uBAAuB,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/D,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,aAAa,CAAC,EAAE,0BAA0B,CAAC,eAAe,CAAC,CAAC;IAC5D,gFAAgF;IAChF,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kFAAkF;IAClF,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uDAAuD;IACvD,UAAU,CAAC,EAAE,oBAAoB,CAAC;CACnC;
|
|
1
|
+
{"version":3,"file":"AgentPanel.d.ts","sourceRoot":"","sources":["../../src/client/AgentPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -314,6 +314,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
314
314
|
/Mac|iPhone|iPad/.test(navigator.userAgent), []);
|
|
315
315
|
const closeTabHint = isMac ? "\u2303W" : "Alt+W";
|
|
316
316
|
const closeAllTabsHint = isMac ? "\u2303\u2325W" : "Ctrl+Alt+W";
|
|
317
|
+
const toggleSidebarHint = isMac ? "\u2318\\" : "Ctrl+\\";
|
|
317
318
|
const [execMode, setExecMode] = useState(() => {
|
|
318
319
|
try {
|
|
319
320
|
const saved = localStorage.getItem(execModeKey);
|
|
@@ -538,7 +539,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
538
539
|
const [headerMenuOpen, setHeaderMenuOpen] = useState(false);
|
|
539
540
|
const [feedbackOpen, setFeedbackOpen] = useState(false);
|
|
540
541
|
const renderHeaderActions = useCallback(({ activeChatSessionId, activeTabId, addTab, clearActiveTab, closeAllTabs, closeOtherTabs, closeTab, showHistory, tabs, toggleHistory, }) => (_jsxs("div", { className: "relative flex shrink-0 items-center gap-0.5", children: [SHOW_ONBOARDING && (_jsx(Suspense, { fallback: null, children: _jsx(SetupButton, {}) })), _jsx(FeedbackButton, { variant: "icon", side: "bottom", align: "end", chatSessionId: activeChatSessionId, chatStorageKey: storageKey, open: feedbackOpen, onOpenChange: setFeedbackOpen, trigger: _jsx("button", { type: "button", tabIndex: -1, "aria-hidden": "true", className: "pointer-events-none absolute end-0 top-full h-px w-px opacity-0" }) }), mode === "chat" && (_jsx(IconTooltip, { content: t("agentPanel.newChat"), children: _jsx("button", { onClick: addTab, "aria-label": t("agentPanel.newChat"), className: "agent-sidebar-hover-reveal flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 14 }) }) })), mode === "cli" && canUseCodeTools && (_jsx(IconTooltip, { content: t("agentPanel.newTerminal"), children: _jsx("button", { onClick: addCliTab, "aria-label": t("agentPanel.newTerminal"), className: "agent-sidebar-hover-reveal flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 14 }) }) })), _jsxs(DropdownMenu, { open: headerMenuOpen, onOpenChange: setHeaderMenuOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: cn("flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", (headerMenuOpen || mode === "settings") &&
|
|
541
|
-
"bg-accent text-foreground"), "aria-label": t("agentPanel.panelOptions"), children: _jsx(IconDotsVertical, { size: 14 }) }) }), _jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "w-48", children: [onCollapse && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: onCollapse, children: [_jsx(IconLayoutSidebarRightCollapse, { size: 14, className: "shrink-0" }), t("agentPanel.collapseSidebar")] }), _jsx(DropdownMenuSeparator, {})] })), mode === "chat" && toggleHistory && (_jsxs(DropdownMenuItem, { onSelect: toggleHistory, children: [_jsx(IconHistory, { size: 14, className: "shrink-0" }), showHistory
|
|
542
|
+
"bg-accent text-foreground"), "aria-label": t("agentPanel.panelOptions"), children: _jsx(IconDotsVertical, { size: 14 }) }) }), _jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "w-48", children: [onCollapse && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: onCollapse, children: [_jsx(IconLayoutSidebarRightCollapse, { size: 14, className: "shrink-0" }), t("agentPanel.collapseSidebar"), _jsx(DropdownMenuShortcut, { children: toggleSidebarHint })] }), _jsx(DropdownMenuSeparator, {})] })), mode === "chat" && toggleHistory && (_jsxs(DropdownMenuItem, { onSelect: toggleHistory, children: [_jsx(IconHistory, { size: 14, className: "shrink-0" }), showHistory
|
|
542
543
|
? t("agentPanel.hideChats")
|
|
543
544
|
: t("agentPanel.allChats")] })), mode === "chat" && (_jsx(RunsTrayMenuItem, { pollMs: 2000, limit: 12, showRecent: true, onOpenThread: openRunThread })), mode === "chat" && _jsx(DropdownMenuSeparator, {}), mode === "cli" && availableClis.length > 0 && (_jsxs(_Fragment, { children: [availableClis.map((cli) => (_jsxs(DropdownMenuItem, { onSelect: () => selectCli(cli.command), className: cn(cli.command === selectedCli
|
|
544
545
|
? "font-medium"
|