@agent-native/core 0.191.0 → 0.192.0-nightly-20260926045737

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 (78) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +4 -0
  3. package/corpus/templates/analytics/app/components/layout/Layout.tsx +41 -12
  4. package/corpus/templates/analytics/app/lib/chat-handoff.ts +62 -0
  5. package/corpus/templates/analytics/changelog/2026-09-25-chat-sidebar-stays-closed-in-settings.md +6 -0
  6. package/corpus/templates/analytics/changelog/2026-09-25-show-one-recent-prompt-per-chat-turn.md +6 -0
  7. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +74 -11
  8. package/corpus/templates/clips/app/i18n/en-US.ts +6 -0
  9. package/corpus/templates/clips/changelog/2026-09-25-one-click-builder-storage.md +6 -0
  10. package/corpus/templates/design/actions/get-design.schema.ts +19 -0
  11. package/corpus/templates/design/actions/get-design.ts +16 -19
  12. package/corpus/templates/mail/actions/apply-ai-filter.ts +8 -0
  13. package/corpus/templates/mail/actions/create-automation.ts +7 -1
  14. package/corpus/templates/mail/actions/manage-ai-filter-rule-undo.ts +34 -0
  15. package/corpus/templates/mail/actions/manage-automations.ts +21 -44
  16. package/corpus/templates/mail/app/components/email/EmailList.tsx +35 -0
  17. package/corpus/templates/mail/app/components/onboarding/AiInboxSetup.tsx +116 -34
  18. package/corpus/templates/mail/app/components/settings/AiFilterSection.tsx +108 -70
  19. package/corpus/templates/mail/app/components/settings/AiRulePromptField.tsx +8 -1
  20. package/corpus/templates/mail/app/components/settings/JevConnectionPrompt.tsx +171 -0
  21. package/corpus/templates/mail/app/hooks/use-automations.ts +24 -0
  22. package/corpus/templates/mail/app/i18n/en-US.ts +8 -2
  23. package/corpus/templates/mail/app/pages/InboxPage.tsx +12 -2
  24. package/corpus/templates/mail/changelog/2026-09-25-mail-now-guides-users-to-connect-jev-before-setting-up-triag.md +6 -0
  25. package/corpus/templates/mail/server/db/schema.ts +17 -0
  26. package/corpus/templates/mail/server/lib/ai-filter-rule-undo.ts +151 -0
  27. package/corpus/templates/mail/server/lib/automations.ts +34 -0
  28. package/corpus/templates/mail/server/plugins/db.ts +18 -0
  29. package/corpus/templates/mail/server/plugins/mail-jobs.ts +6 -0
  30. package/dist/client/AssistantChat.js +20 -3
  31. package/dist/client/BuilderReferralInviteRow.d.ts +3 -0
  32. package/dist/client/BuilderReferralInviteRow.js +16 -0
  33. package/dist/client/agent-chat-adapter.js +6 -5
  34. package/dist/client/chat/run-recovery.js +6 -5
  35. package/dist/client/settings/UsageSection.js +2 -1
  36. package/dist/client/settings/index.d.ts +1 -1
  37. package/dist/client/settings/index.js +1 -1
  38. package/dist/client/settings/useBuilderStatus.d.ts +4 -1
  39. package/dist/client/settings/useBuilderStatus.js +32 -16
  40. package/dist/client/use-agent-chat-running-threads.d.ts +2 -0
  41. package/dist/collab/struct-routes.d.ts +1 -1
  42. package/dist/framework-tools.d.ts +3 -1
  43. package/dist/framework-tools.js +2 -0
  44. package/dist/localization/core-messages/ar-SA.js +4 -0
  45. package/dist/localization/core-messages/de-DE.js +4 -0
  46. package/dist/localization/core-messages/en-US.d.ts +4 -0
  47. package/dist/localization/core-messages/en-US.js +4 -0
  48. package/dist/localization/core-messages/es-ES.js +4 -0
  49. package/dist/localization/core-messages/fr-FR.js +4 -0
  50. package/dist/localization/core-messages/hi-IN.js +4 -0
  51. package/dist/localization/core-messages/ja-JP.js +4 -0
  52. package/dist/localization/core-messages/ko-KR.js +4 -0
  53. package/dist/localization/core-messages/pt-BR.js +4 -0
  54. package/dist/localization/core-messages/zh-CN.js +4 -0
  55. package/dist/localization/core-messages/zh-TW.js +4 -0
  56. package/dist/localization/core-messages.d.ts +4 -0
  57. package/dist/notifications/routes.d.ts +3 -3
  58. package/dist/observability/routes.d.ts +5 -5
  59. package/dist/progress/routes.d.ts +1 -1
  60. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  61. package/dist/provider-api/actions/provider-api.d.ts +14 -14
  62. package/dist/secrets/routes.d.ts +3 -3
  63. package/dist/server/action-discovery.js +4 -0
  64. package/dist/server/agent-chat/plugin-options.d.ts +1 -1
  65. package/dist/server/agent-chat-plugin.js +6 -4
  66. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  67. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  68. package/dist/server/core-routes-plugin.d.ts +1 -1
  69. package/dist/server/fusion-app.d.ts +3 -0
  70. package/dist/server/fusion-app.js +17 -0
  71. package/dist/shared/builder-referrals.d.ts +10 -0
  72. package/dist/shared/builder-referrals.js +29 -0
  73. package/dist/usage/actions/get-builder-referral-info.d.ts +9 -0
  74. package/dist/usage/actions/get-builder-referral-info.js +26 -0
  75. package/dist/usage/metrics-store.js +142 -30
  76. package/dist/vite/action-types-plugin.js +4 -0
  77. package/docs/content/actions-agent-tools.mdx +2 -1
  78. package/package.json +3 -2
@@ -39,6 +39,7 @@ import { toast } from "sonner";
39
39
  import { AiFilterDialog } from "@/components/email/AiFilterDialog";
40
40
  import { GoogleConnectBanner } from "@/components/GoogleConnectBanner";
41
41
  import { useSetHeaderActions } from "@/components/layout/HeaderActions";
42
+ import { JevConnectionPrompt } from "@/components/settings/JevConnectionPrompt";
42
43
  import {
43
44
  DropdownMenu,
44
45
  DropdownMenuContent,
@@ -199,6 +200,11 @@ interface EmailListProps {
199
200
  onDraftOpen?: (email: EmailMessage) => void;
200
201
  onNavigateThread?: (threadId: string) => void;
201
202
  showPrioritySort?: boolean;
203
+ jevConfigured?: boolean;
204
+ jevAvailabilityLoading?: boolean;
205
+ jevAvailabilityError?: boolean;
206
+ onJevConnected?: () => void;
207
+ onJevRetry?: () => void;
202
208
  sortMode?: MailSortMode;
203
209
  onSortModeChange?: (mode: MailSortMode) => void;
204
210
  }
@@ -521,6 +527,11 @@ export function EmailList({
521
527
  onDraftOpen,
522
528
  onNavigateThread,
523
529
  showPrioritySort = false,
530
+ jevConfigured = false,
531
+ jevAvailabilityLoading = false,
532
+ jevAvailabilityError = false,
533
+ onJevConnected,
534
+ onJevRetry,
524
535
  sortMode = "newest",
525
536
  onSortModeChange,
526
537
  }: EmailListProps) {
@@ -2198,6 +2209,25 @@ export function EmailList({
2198
2209
  </DropdownMenuItem>
2199
2210
  </div>
2200
2211
  )}
2212
+ {!showPrioritySort &&
2213
+ (jevAvailabilityError ? (
2214
+ <DropdownMenuItem
2215
+ onSelect={onJevRetry}
2216
+ disabled={jevAvailabilityLoading}
2217
+ className="justify-between"
2218
+ >
2219
+ {t("mail.sort.priority")}
2220
+ <span className="text-xs text-muted-foreground">
2221
+ {t("mail.error.tryAgain")}
2222
+ </span>
2223
+ </DropdownMenuItem>
2224
+ ) : (
2225
+ <JevConnectionPrompt
2226
+ variant="menu-item"
2227
+ disabled={jevAvailabilityLoading}
2228
+ onConnected={onJevConnected}
2229
+ />
2230
+ ))}
2201
2231
  </DropdownMenuContent>
2202
2232
  </DropdownMenu>
2203
2233
  </div>
@@ -2207,6 +2237,11 @@ export function EmailList({
2207
2237
  labelParam,
2208
2238
  navigate,
2209
2239
  showPrioritySort,
2240
+ jevConfigured,
2241
+ jevAvailabilityLoading,
2242
+ jevAvailabilityError,
2243
+ onJevConnected,
2244
+ onJevRetry,
2210
2245
  currentSortMode,
2211
2246
  onSortModeChange,
2212
2247
  searchQuery,
@@ -1,11 +1,17 @@
1
+ import { useActionQuery } from "@agent-native/core/client/hooks";
1
2
  import { useT } from "@agent-native/core/client/i18n";
2
3
  import { AI_FILTER_LABEL } from "@shared/ai-filter";
3
4
  import { AI_IMPORTANT_LABEL } from "@shared/ai-priority";
4
5
  import type { AutomationAction } from "@shared/types";
5
- import { useMemo, useState } from "react";
6
+ import { IconCheck } from "@tabler/icons-react";
7
+ import { useEffect, useMemo, useRef, useState } from "react";
6
8
  import { toast } from "sonner";
7
9
 
8
10
  import { AiRulePromptField } from "@/components/settings/AiRulePromptField";
11
+ import {
12
+ JevAvailabilityError,
13
+ JevConnectionPrompt,
14
+ } from "@/components/settings/JevConnectionPrompt";
9
15
  import { Button } from "@/components/ui/button";
10
16
  import {
11
17
  Dialog,
@@ -35,7 +41,7 @@ export const TAG_SUGGESTIONS = [
35
41
  ["finance", "mail.sort.aiSetupTagFinance", "mail.sort.aiSetupPromptFinance"],
36
42
  ] as const;
37
43
 
38
- type SetupStep = 0 | 1 | 2;
44
+ type SetupStep = -1 | 0 | 1 | 2;
39
45
 
40
46
  export function AiInboxSetup({
41
47
  forceOpen = false,
@@ -49,11 +55,26 @@ export function AiInboxSetup({
49
55
  const { data: rules = [], isLoading: rulesLoading } = useAutomations();
50
56
  const { data: labels = [] } = useLabels();
51
57
  const googleStatus = useGoogleAuthStatus();
58
+ const connected = (googleStatus.data?.accounts.length ?? 0) > 0;
59
+ const jevAvailability = useActionQuery(
60
+ "get-jev-availability",
61
+ {},
62
+ {
63
+ enabled: connected,
64
+ staleTime: 0,
65
+ // request-storm-allow: the shared status query revalidates API-key setup when its settings tab returns.
66
+ refetchOnWindowFocus: true,
67
+ },
68
+ );
69
+ const jevAvailabilityResolved =
70
+ !jevAvailability.isError && jevAvailability.data != null;
71
+ const jevConfigured =
72
+ jevAvailabilityResolved && jevAvailability.data?.configured === true;
52
73
  const createRuleMutation = useCreateAutomation();
53
74
  const updateSettings = useUpdateSettings();
54
75
  const [step, setStep] = useState<SetupStep>(0);
55
76
  const [selectedTags, setSelectedTags] = useState(
56
- () => new Set<string>(TAG_SUGGESTIONS.map(([id]) => id)),
77
+ () => new Set<string>(["receipts", "github"]),
57
78
  );
58
79
  const [customTagSelected, setCustomTagSelected] = useState(false);
59
80
  const [customTagName, setCustomTagName] = useState("");
@@ -61,13 +82,11 @@ export function AiInboxSetup({
61
82
  const [importantPrompt, setImportantPrompt] = useState(() =>
62
83
  t("mail.sort.aiSetupImportantPrompt"),
63
84
  );
64
- const [archivePrompt, setArchivePrompt] = useState(() =>
65
- t("mail.aiFilter.archivePlaceholder"),
66
- );
67
- const [spamPrompt, setSpamPrompt] = useState(() =>
68
- t("mail.aiFilter.spamPlaceholder"),
69
- );
85
+ const [archivePrompt, setArchivePrompt] = useState("");
86
+ const [spamPrompt, setSpamPrompt] = useState("");
70
87
  const [saving, setSaving] = useState(false);
88
+ const [jevStepRequired, setJevStepRequired] = useState(false);
89
+ const previousForceOpen = useRef(forceOpen);
71
90
 
72
91
  const aiRules = useMemo(
73
92
  () =>
@@ -76,14 +95,30 @@ export function AiInboxSetup({
76
95
  ),
77
96
  [rules],
78
97
  );
79
- const connected = (googleStatus.data?.accounts.length ?? 0) > 0;
80
98
  const visible =
81
99
  connected &&
82
100
  !googleStatus.isLoading &&
83
101
  !rulesLoading &&
102
+ !jevAvailability.isLoading &&
84
103
  (forceOpen ||
85
104
  (settings?.aiSetupCompleted !== true && aiRules.length === 0));
86
105
 
106
+ useEffect(() => {
107
+ const wasForceOpen = previousForceOpen.current;
108
+ if (!forceOpen) previousForceOpen.current = false;
109
+ if (!visible || !jevAvailabilityResolved) return;
110
+ previousForceOpen.current = forceOpen;
111
+ if (forceOpen && !wasForceOpen) {
112
+ setJevStepRequired(!jevConfigured);
113
+ setStep(jevConfigured ? 0 : -1);
114
+ } else if (!jevConfigured) {
115
+ setJevStepRequired(true);
116
+ setStep(-1);
117
+ } else if (step === -1) {
118
+ setStep(0);
119
+ }
120
+ }, [forceOpen, jevAvailabilityResolved, jevConfigured, step, visible]);
121
+
87
122
  const complete = async () => {
88
123
  try {
89
124
  await updateSettings.mutateAsync({ aiSetupCompleted: true });
@@ -115,6 +150,7 @@ export function AiInboxSetup({
115
150
  };
116
151
 
117
152
  const saveStep = async () => {
153
+ if (step < 0 || !jevConfigured) return;
118
154
  setSaving(true);
119
155
  try {
120
156
  if (step === 0) {
@@ -176,6 +212,15 @@ export function AiInboxSetup({
176
212
  };
177
213
 
178
214
  const skip = async () => {
215
+ if (step < 0) {
216
+ await complete();
217
+ return;
218
+ }
219
+ if (!jevAvailabilityResolved) return;
220
+ if (!jevConfigured) {
221
+ await complete();
222
+ return;
223
+ }
179
224
  if (step < 2) {
180
225
  setStep((current) => (current + 1) as SetupStep);
181
226
  return;
@@ -184,11 +229,17 @@ export function AiInboxSetup({
184
229
  };
185
230
 
186
231
  const headline =
187
- step === 0
188
- ? t("mail.sort.aiSetupTagsHeadline")
189
- : step === 1
190
- ? t("mail.sort.aiSetupImportantHeadline")
191
- : t("mail.sort.aiSetupSkipInboxHeadline");
232
+ step === -1
233
+ ? jevAvailability.isError
234
+ ? t("mail.aiFilter.jevAvailabilityFailed")
235
+ : t("mail.aiFilter.connectJev")
236
+ : step === 0
237
+ ? t("mail.sort.aiSetupTagsHeadline")
238
+ : step === 1
239
+ ? t("mail.sort.aiSetupImportantHeadline")
240
+ : t("mail.sort.aiSetupSkipInboxHeadline");
241
+ const stepCount = jevStepRequired ? 4 : 3;
242
+ const progressIndex = step < 0 ? 0 : step + (jevStepRequired ? 1 : 0);
192
243
  const customTagIncomplete =
193
244
  step === 0 &&
194
245
  customTagSelected &&
@@ -213,16 +264,39 @@ export function AiInboxSetup({
213
264
  </DialogHeader>
214
265
  <div
215
266
  className="mb-8 flex items-center gap-2"
216
- aria-label={`${step + 1}/3`}
267
+ aria-label={`${progressIndex + 1}/${stepCount}`}
217
268
  >
218
- {[0, 1, 2].map((index) => (
219
- <span
220
- key={index}
221
- className={`h-1 flex-1 rounded-full ${index <= step ? "bg-primary" : "bg-muted"}`}
222
- />
223
- ))}
269
+ {Array.from({ length: stepCount }, (_, index) => index).map(
270
+ (index) => (
271
+ <span
272
+ key={index}
273
+ className={`h-1 flex-1 rounded-full ${index <= progressIndex ? "bg-primary" : "bg-muted"}`}
274
+ />
275
+ ),
276
+ )}
224
277
  </div>
225
- {step === 0 ? (
278
+ {step >= 0 && jevAvailability.isError ? (
279
+ <div className="mb-5">
280
+ <JevAvailabilityError
281
+ onRetry={() => void jevAvailability.refetch()}
282
+ retrying={jevAvailability.isFetching}
283
+ />
284
+ </div>
285
+ ) : null}
286
+ {step === -1 ? (
287
+ jevAvailability.isError ? (
288
+ <JevAvailabilityError
289
+ onRetry={() => void jevAvailability.refetch()}
290
+ retrying={jevAvailability.isFetching}
291
+ showMessage={false}
292
+ />
293
+ ) : (
294
+ <JevConnectionPrompt
295
+ showHeading={false}
296
+ onConnected={() => void jevAvailability.refetch()}
297
+ />
298
+ )
299
+ ) : step === 0 ? (
226
300
  <div className="space-y-4">
227
301
  <div className="flex flex-wrap gap-2">
228
302
  {TAG_SUGGESTIONS.map(([id, nameKey]) => {
@@ -240,8 +314,9 @@ export function AiInboxSetup({
240
314
  return next;
241
315
  })
242
316
  }
243
- className={`rounded-full border px-4 py-2 text-sm transition-colors ${selected ? "border-primary bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground"}`}
317
+ className={`inline-flex items-center gap-1.5 rounded-full border px-4 py-2 text-sm transition-colors ${selected ? "border-primary bg-primary text-primary-foreground" : "border-border text-muted-foreground hover:text-foreground"}`}
244
318
  >
319
+ {selected ? <IconCheck className="size-3.5" /> : null}
245
320
  {t(nameKey)}
246
321
  </button>
247
322
  );
@@ -250,8 +325,11 @@ export function AiInboxSetup({
250
325
  type="button"
251
326
  aria-pressed={customTagSelected}
252
327
  onClick={() => setCustomTagSelected((selected) => !selected)}
253
- className={`rounded-full border px-4 py-2 text-sm transition-colors ${customTagSelected ? "border-primary bg-primary/10 text-foreground" : "border-border text-muted-foreground hover:text-foreground"}`}
328
+ className={`inline-flex items-center gap-1.5 rounded-full border px-4 py-2 text-sm transition-colors ${customTagSelected ? "border-primary bg-primary text-primary-foreground" : "border-border text-muted-foreground hover:text-foreground"}`}
254
329
  >
330
+ {customTagSelected ? (
331
+ <IconCheck className="size-3.5" />
332
+ ) : null}
255
333
  {t("mail.sort.aiSetupCustomTag")}
256
334
  </button>
257
335
  </div>
@@ -287,6 +365,7 @@ export function AiInboxSetup({
287
365
  value={archivePrompt}
288
366
  onChange={setArchivePrompt}
289
367
  label={t("mail.sort.aiSetupArchiveLabel")}
368
+ placeholder={t("mail.aiFilter.archivePlaceholder")}
290
369
  className="min-h-28 resize-none text-sm font-normal"
291
370
  />
292
371
  </label>
@@ -296,6 +375,7 @@ export function AiInboxSetup({
296
375
  value={spamPrompt}
297
376
  onChange={setSpamPrompt}
298
377
  label={t("mail.sort.aiSetupSpamLabel")}
378
+ placeholder={t("mail.aiFilter.spamPlaceholder")}
299
379
  className="min-h-28 resize-none text-sm font-normal"
300
380
  />
301
381
  </label>
@@ -317,19 +397,21 @@ export function AiInboxSetup({
317
397
  <Button
318
398
  variant="ghost"
319
399
  onClick={() => void skip()}
320
- disabled={saving}
400
+ disabled={saving || (step >= 0 && !jevAvailabilityResolved)}
321
401
  >
322
402
  {t("mail.sort.aiSetupSkip")}
323
403
  </Button>
324
404
  </div>
325
- <Button
326
- onClick={() => void saveStep()}
327
- disabled={saving || customTagIncomplete}
328
- >
329
- {step === 2
330
- ? t("mail.sort.aiSetupDone")
331
- : t("mail.sort.aiSetupContinue")}
332
- </Button>
405
+ {step >= 0 && (
406
+ <Button
407
+ onClick={() => void saveStep()}
408
+ disabled={saving || !jevConfigured || customTagIncomplete}
409
+ >
410
+ {step === 2
411
+ ? t("mail.sort.aiSetupDone")
412
+ : t("mail.sort.aiSetupContinue")}
413
+ </Button>
414
+ )}
333
415
  </div>
334
416
  </div>
335
417
  </DialogContent>