@agent-native/core 0.186.0 → 0.187.0-nightly-20260923203303

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 (44) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/clips/app/components/library/page-header.tsx +5 -37
  3. package/corpus/templates/clips/app/i18n/en-US.ts +0 -1
  4. package/corpus/templates/mail/AGENTS.md +1 -1
  5. package/corpus/templates/mail/actions/get-ai-priority.ts +26 -9
  6. package/corpus/templates/mail/actions/get-jev-availability.ts +23 -0
  7. package/corpus/templates/mail/actions/navigate.ts +19 -2
  8. package/corpus/templates/mail/app/components/email/EmailList.tsx +13 -4
  9. package/corpus/templates/mail/app/pages/InboxPage.tsx +47 -6
  10. package/corpus/templates/mail/changelog/2026-09-22-jev-powered-mail-rules-and-priority-sort-work-with-an-enable.md +6 -0
  11. package/corpus/templates/mail/changelog/2026-09-23-preserve-priority-sort-on-availability-error.md +6 -0
  12. package/corpus/templates/mail/server/lib/automation-engine.ts +147 -132
  13. package/corpus/templates/mail/server/lib/automation-model.ts +32 -1
  14. package/dist/agent/jev-tool-prefetch.d.ts +27 -3
  15. package/dist/agent/jev-tool-prefetch.js +103 -23
  16. package/dist/agent/production-agent.d.ts +9 -0
  17. package/dist/agent/production-agent.js +55 -23
  18. package/dist/app-config/integrations.d.ts +1 -0
  19. package/dist/app-config/integrations.js +7 -0
  20. package/dist/app-config/schema.d.ts +1 -0
  21. package/dist/automation/index.d.ts +1 -1
  22. package/dist/integrations/a2a-continuation-processor.js +35 -3
  23. package/dist/integrations/a2a-continuations-store.d.ts +2 -0
  24. package/dist/integrations/a2a-continuations-store.js +44 -2
  25. package/dist/integrations/integration-campaign-recovery.js +13 -2
  26. package/dist/integrations/integration-campaigns-store.d.ts +1 -0
  27. package/dist/integrations/integration-campaigns-store.js +18 -0
  28. package/dist/integrations/integration-durable-dispatch-config.js +2 -8
  29. package/dist/integrations/integration-durable-dispatch.d.ts +2 -0
  30. package/dist/integrations/integration-durable-dispatch.js +20 -4
  31. package/dist/integrations/plugin.js +11 -1
  32. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  33. package/dist/notifications/routes.d.ts +3 -3
  34. package/dist/resources/handlers.d.ts +1 -1
  35. package/dist/secrets/routes.d.ts +6 -6
  36. package/dist/server/agent-chat/prompt-resources.d.ts +1 -0
  37. package/dist/server/agent-chat/prompt-resources.js +5 -1
  38. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  39. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  40. package/dist/server/credential-provider.d.ts +1 -1
  41. package/dist/server/credential-provider.js +4 -3
  42. package/dist/server/index.d.ts +2 -1
  43. package/dist/server/index.js +2 -1
  44. package/package.json +3 -3
package/corpus/README.md CHANGED
@@ -31,4 +31,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
31
31
 
32
32
  ## Generated Counts
33
33
 
34
- - template files: 9796
34
+ - template files: 9799
@@ -1,5 +1,3 @@
1
- import { useT } from "@agent-native/core/client/i18n";
2
- import { IconArrowLeft } from "@tabler/icons-react";
3
1
  import {
4
2
  type ComponentProps,
5
3
  createContext,
@@ -108,9 +106,8 @@ export interface PageBreadcrumbItem {
108
106
  /**
109
107
  * Collapses breadcrumb segments only as far as the available header width
110
108
  * actually requires. Segments closest to the root are hidden first, so the
111
- * current page and the ancestors nearest it — including the parent the back
112
- * button returns to — stay visible: a long path degrades to
113
- * "Root / … / Parent / Current" rather than hiding where "back" leads.
109
+ * current page and its immediate parent stay visible: a long path degrades
110
+ * to "Root / … / Parent / Current" rather than hiding the nearest ancestor.
114
111
  * Re-measures on resize so widening the window (or collapsing the sidebar)
115
112
  * brings hidden segments back.
116
113
  */
@@ -118,7 +115,7 @@ function useBreadcrumbOverflow(itemCount: number) {
118
115
  const listRef = useRef<HTMLOListElement>(null);
119
116
  const [hiddenCount, setHiddenCount] = useState(0);
120
117
  // Middle segments are everything strictly between the root and the
121
- // current page; the parent (the one right before current) is never hidden.
118
+ // current page; the immediate parent is never hidden.
122
119
  const maxHidden = Math.max(0, itemCount - 2 - 1);
123
120
 
124
121
  // The path changed — start fully expanded and let the measurement below
@@ -152,18 +149,14 @@ export function PageBreadcrumb({
152
149
  }: {
153
150
  items: readonly PageBreadcrumbItem[];
154
151
  }) {
155
- const t = useT();
156
152
  const { listRef, hiddenCount } = useBreadcrumbOverflow(items.length);
157
153
 
158
154
  if (items.length === 0) return null;
159
155
 
160
- // The item right before the current one is one step back in the path; it
161
- // only exists on a nested path, so root-level trails render no back button.
162
- const parent = items[items.length - 2];
163
156
  const fullPath = items.map((item) => item.label).join(" / ");
164
157
  const collapsed = hiddenCount > 0;
165
158
  // Keep the root as an anchor, drop the segments right after it, and keep the
166
- // whole tail (parent + current) so the back target stays on screen.
159
+ // whole tail (parent + current) visible.
167
160
  const visibleItems = collapsed
168
161
  ? [items[0], ...items.slice(1 + hiddenCount)]
169
162
  : items;
@@ -211,7 +204,7 @@ export function PageBreadcrumb({
211
204
  );
212
205
 
213
206
  // The tooltip only adds value once ancestors are actually hidden.
214
- const trail = collapsed ? (
207
+ return collapsed ? (
215
208
  <Tooltip>
216
209
  <TooltipTrigger asChild>{breadcrumb}</TooltipTrigger>
217
210
  <TooltipContent>{fullPath}</TooltipContent>
@@ -219,29 +212,4 @@ export function PageBreadcrumb({
219
212
  ) : (
220
213
  breadcrumb
221
214
  );
222
-
223
- if (!parent?.to) return trail;
224
-
225
- const backLabel = t("navigation.breadcrumbBack", { label: parent.label });
226
-
227
- return (
228
- <div className="flex min-w-0 items-center gap-1">
229
- <Tooltip>
230
- <TooltipTrigger asChild>
231
- <Button
232
- asChild
233
- variant="ghost"
234
- size="icon"
235
- className="size-7 shrink-0 text-muted-foreground"
236
- >
237
- <NavLink to={parent.to} aria-label={backLabel}>
238
- <IconArrowLeft className="size-4" />
239
- </NavLink>
240
- </Button>
241
- </TooltipTrigger>
242
- <TooltipContent>{backLabel}</TooltipContent>
243
- </Tooltip>
244
- {trail}
245
- </div>
246
- );
247
215
  }
@@ -80,7 +80,6 @@ const messages = {
80
80
  createFolderError: "Create failed",
81
81
  folderCreated: "Folder created",
82
82
  folderNamePlaceholder: "Folder name",
83
- breadcrumbBack: "Back to {{label}}",
84
83
  },
85
84
  empty: {
86
85
  library: {
@@ -76,7 +76,7 @@ Read the relevant skill before deeper work:
76
76
  | `manage-automations` | Recurring and event-triggered automations shown in Settings. |
77
77
  | `manage-email-rules` / `trigger-automations` | Inbox automation rules. |
78
78
  | `get-ai-filter` / `apply-ai-filter` | Reversible AI filtering, feedback, and learned instructions. |
79
- | `get-ai-priority` | Cache Jev/Luna scores for up to 500 Inbox emails. |
79
+ | `get-ai-priority` | Optional Jev Priority sort; requires Jev access. |
80
80
  | `respond-calendar-invite` | Accept/decline/tentative an invite. |
81
81
  | `get-mail-settings` / `update-mail-settings` / `import-gmail-signature` | Drafting preferences, including Send + Mark Done. |
82
82
  | `manage-snippets` | Saved reply snippets. |
@@ -1,7 +1,11 @@
1
1
  import { createHash } from "node:crypto";
2
2
 
3
- import { defineAction } from "@agent-native/core/action";
4
- import { getRequestUserEmail } from "@agent-native/core/server";
3
+ import { defineAction, fail } from "@agent-native/core/action";
4
+ import {
5
+ getJevContextCredentials,
6
+ getRequestUserEmail,
7
+ isJevEnabled,
8
+ } from "@agent-native/core/server";
5
9
  import { z } from "zod";
6
10
 
7
11
  import {
@@ -11,10 +15,12 @@ import {
11
15
  saveAiPriorityCache,
12
16
  type AiPriorityCacheEntry,
13
17
  } from "../server/lib/ai-priority.js";
18
+ import { previewAutomationPriority } from "../server/lib/automation-engine.js";
14
19
  import {
15
- getAutomationModelSettings,
16
- previewAutomationPriority,
17
- } from "../server/lib/automation-engine.js";
20
+ TYPESAFE_AUTOMATION_ENGINE,
21
+ TYPESAFE_AUTOMATION_MODEL,
22
+ type AutomationModelSettings,
23
+ } from "../server/lib/automation-model.js";
18
24
  import { listAutomationRules } from "../server/lib/automations.js";
19
25
  import {
20
26
  AI_IMPORTANT_LABEL,
@@ -64,20 +70,30 @@ function importantRules(
64
70
 
65
71
  export default defineAction({
66
72
  description:
67
- "Score up to 500 newest, non-archived Inbox emails for a cached Priority sort using the Mail Important rules. Archived, auto-archived, trashed, and non-Inbox emails are never evaluated.",
73
+ "Score up to 500 newest, non-archived Inbox emails with Jev for a cached Priority sort using Mail Important rules. Requires Builder Jev access or a direct Jev API key.",
68
74
  schema: z.object({
69
75
  emails: z.array(aiPriorityEmailSchema).max(AI_PRIORITY_MAX_EMAILS),
70
76
  }),
71
77
  agentTool: false,
72
78
  run: async ({ emails }) => {
73
79
  const ownerEmail = getRequestUserEmail();
74
- if (!ownerEmail) throw new Error("Unauthenticated");
80
+ if (!ownerEmail) fail("Unauthenticated", { errorCode: "unauthenticated" });
81
+ const jevCredentials = await getJevContextCredentials(ownerEmail);
82
+ if (!(await isJevEnabled(jevCredentials))) {
83
+ fail("Jev is not enabled for this account.", {
84
+ errorCode: "jev_not_enabled",
85
+ statusCode: 403,
86
+ });
87
+ }
75
88
 
76
89
  const rules = importantRules(await listAutomationRules(ownerEmail));
77
90
  const instruction = rules.length
78
91
  ? rules.map((rule) => rule.condition.trim()).join("\n")
79
92
  : AI_PRIORITY_DEFAULT_INSTRUCTION;
80
- const modelSettings = await getAutomationModelSettings(ownerEmail);
93
+ const modelSettings = {
94
+ engine: TYPESAFE_AUTOMATION_ENGINE,
95
+ model: TYPESAFE_AUTOMATION_MODEL,
96
+ };
81
97
  const instructionKey = hash(
82
98
  JSON.stringify({
83
99
  model: modelSettings,
@@ -108,13 +124,14 @@ export default defineAction({
108
124
  }));
109
125
  const scores = getCachedPriorityScores(cache, fingerprints, instructionKey);
110
126
  const pending = eligibleEmails.filter((email) => !scores.has(email.id));
111
- let model = modelSettings;
127
+ let model: AutomationModelSettings = modelSettings;
112
128
 
113
129
  if (pending.length > 0) {
114
130
  const result = await previewAutomationPriority(
115
131
  pending,
116
132
  ownerEmail,
117
133
  instruction,
134
+ jevCredentials,
118
135
  AbortSignal.timeout(25_000),
119
136
  );
120
137
  model = result.model;
@@ -0,0 +1,23 @@
1
+ import { defineAction } from "@agent-native/core/action";
2
+ import {
3
+ getJevContextCredentials,
4
+ getRequestUserEmail,
5
+ isJevEnabled,
6
+ } from "@agent-native/core/server";
7
+ import { z } from "zod";
8
+
9
+ export default defineAction({
10
+ description:
11
+ "Check whether Jev is configured for this account through Builder or a direct Jev API key.",
12
+ schema: z.object({}),
13
+ outputSchema: z.object({ configured: z.boolean() }),
14
+ http: { method: "GET" },
15
+ readOnly: true,
16
+ agentTool: false,
17
+ run: async () => {
18
+ const ownerEmail = getRequestUserEmail();
19
+ if (!ownerEmail) return { configured: false };
20
+ const credentials = await getJevContextCredentials(ownerEmail);
21
+ return { configured: await isJevEnabled(credentials) };
22
+ },
23
+ });
@@ -1,10 +1,15 @@
1
- import { defineAction } from "@agent-native/core/action";
1
+ import { defineAction, fail } from "@agent-native/core/action";
2
2
  import { writeAppStateForCurrentTab } from "@agent-native/core/application-state";
3
+ import {
4
+ getJevContextCredentials,
5
+ getRequestUserEmail,
6
+ isJevEnabled,
7
+ } from "@agent-native/core/server";
3
8
  import { z } from "zod";
4
9
 
5
10
  export default defineAction({
6
11
  description:
7
- "Navigate the UI to a specific view or email thread. Writes a navigate command to application state which the UI reads and auto-deletes.",
12
+ "Navigate the UI to a specific view, inbox sort, or email thread. Priority sort requires Jev access. Writes a navigate command to application state which the UI reads and auto-deletes.",
8
13
  schema: z.object({
9
14
  view: z
10
15
  .string()
@@ -59,6 +64,18 @@ export default defineAction({
59
64
  }),
60
65
  http: false,
61
66
  run: async (args) => {
67
+ if (args.sort === "priority") {
68
+ const ownerEmail = getRequestUserEmail();
69
+ const credentials = ownerEmail
70
+ ? await getJevContextCredentials(ownerEmail)
71
+ : null;
72
+ if (!credentials || !(await isJevEnabled(credentials))) {
73
+ fail("Priority sort requires Jev to be enabled for this account.", {
74
+ errorCode: "jev_not_enabled",
75
+ statusCode: 403,
76
+ });
77
+ }
78
+ }
62
79
  const tab = args.tab || args.label || args.filter;
63
80
  if (
64
81
  !args.view &&
@@ -146,6 +146,7 @@ interface EmailListProps {
146
146
  onArchived?: (id: string) => void;
147
147
  onDraftOpen?: (email: EmailMessage) => void;
148
148
  onNavigateThread?: (threadId: string) => void;
149
+ showPrioritySort?: boolean;
149
150
  sortMode?: MailSortMode;
150
151
  onSortModeChange?: (mode: MailSortMode) => void;
151
152
  }
@@ -467,6 +468,7 @@ export function EmailList({
467
468
  onArchived,
468
469
  onDraftOpen,
469
470
  onNavigateThread,
471
+ showPrioritySort = false,
470
472
  sortMode = "newest",
471
473
  onSortModeChange,
472
474
  }: EmailListProps) {
@@ -484,7 +486,9 @@ export function EmailList({
484
486
  const searchQuery = searchParams.get("q") ?? undefined;
485
487
  const labelParam = searchParams.get("label");
486
488
  const currentSortMode: MailSortMode =
487
- view === "inbox" && !searchQuery && !labelParam ? sortMode : "newest";
489
+ showPrioritySort && view === "inbox" && !searchQuery && !labelParam
490
+ ? sortMode
491
+ : "newest";
488
492
  const routeSearchSuffix = searchParams.toString()
489
493
  ? `?${searchParams.toString()}`
490
494
  : "";
@@ -1916,7 +1920,7 @@ export function EmailList({
1916
1920
  () =>
1917
1921
  view === "inbox" && !searchQuery && !labelParam && threads.length > 0 ? (
1918
1922
  <Select
1919
- value={sortMode}
1923
+ value={currentSortMode}
1920
1924
  onValueChange={(value) => onSortModeChange?.(value as MailSortMode)}
1921
1925
  >
1922
1926
  <SelectTrigger
@@ -1929,16 +1933,21 @@ export function EmailList({
1929
1933
  </SelectTrigger>
1930
1934
  <SelectContent align="end">
1931
1935
  <SelectItem value="newest">{t("mail.sort.newest")}</SelectItem>
1932
- <SelectItem value="priority">{t("mail.sort.priority")}</SelectItem>
1936
+ {showPrioritySort && (
1937
+ <SelectItem value="priority">
1938
+ {t("mail.sort.priority")}
1939
+ </SelectItem>
1940
+ )}
1933
1941
  </SelectContent>
1934
1942
  </Select>
1935
1943
  ) : null,
1936
1944
  [
1937
1945
  isPriorityPending,
1938
1946
  labelParam,
1947
+ showPrioritySort,
1948
+ currentSortMode,
1939
1949
  onSortModeChange,
1940
1950
  searchQuery,
1941
- sortMode,
1942
1951
  t,
1943
1952
  threads.length,
1944
1953
  view,
@@ -1,3 +1,4 @@
1
+ import { useActionQuery } from "@agent-native/core/client/hooks";
1
2
  import { useT } from "@agent-native/core/client/i18n";
2
3
  import { normalizeDocumentTitle } from "@agent-native/core/shared";
3
4
  import { AI_PRIORITY_MAX_EMAILS, type MailSortMode } from "@shared/ai-priority";
@@ -10,6 +11,7 @@ import { inboxTabHref } from "@shared/inbox-threads";
10
11
  import type { EmailMessage } from "@shared/types";
11
12
  import { useState, useCallback, useMemo, useEffect, useRef } from "react";
12
13
  import { useParams, useNavigate, useSearchParams } from "react-router";
14
+ import { toast } from "sonner";
13
15
 
14
16
  import { EmailList, InboxZero } from "@/components/email/EmailList";
15
17
  import { EmailThread } from "@/components/email/EmailThread";
@@ -350,6 +352,18 @@ export function InboxPage() {
350
352
  isLoading: settingsLoading,
351
353
  isError: settingsError,
352
354
  } = useSettings();
355
+ const jevAvailability = useActionQuery(
356
+ "get-jev-availability",
357
+ {},
358
+ {
359
+ enabled: view === "inbox" || navState.command.data?.sort === "priority",
360
+ staleTime: 60_000,
361
+ retry: 2,
362
+ },
363
+ );
364
+ const jevConfigured = jevAvailability.data?.configured === true;
365
+ const showPrioritySort =
366
+ jevConfigured || (jevAvailability.isError && sortMode === "priority");
353
367
  const [searchParams] = useSearchParams();
354
368
  const activeLabel = searchParams.get("label");
355
369
  const activeInboxTab = searchParams.get("tab");
@@ -442,6 +456,15 @@ export function InboxPage() {
442
456
  // `useEmails` search path non-inbox views use — the store path is only
443
457
  // for a plain /inbox with no `q`.
444
458
  const isInboxView = view === "inbox" && !searchParams.get("q");
459
+ useEffect(() => {
460
+ if (!jevAvailability.isSuccess) return;
461
+ if (!jevConfigured && sortMode === "priority") setSortMode("newest");
462
+ }, [jevAvailability.isSuccess, jevConfigured, sortMode]);
463
+ useEffect(() => {
464
+ if (jevAvailability.isError && sortMode === "priority") {
465
+ toast.error(t("mail.sort.priorityFailed"));
466
+ }
467
+ }, [jevAvailability.isError, sortMode, t]);
445
468
  useEffect(() => {
446
469
  if (!isInboxView || activeLabel || searchQuery) setSortMode("newest");
447
470
  }, [activeLabel, isInboxView, searchQuery]);
@@ -470,9 +493,17 @@ export function InboxPage() {
470
493
  Math.ceil(AI_PRIORITY_MAX_EMAILS / INBOX_PAGE_SIZE) - 1,
471
494
  );
472
495
  setInboxExtraPageCount(
473
- isInboxView && sortMode === "priority" ? priorityExtraPages : 0,
496
+ showPrioritySort && isInboxView && sortMode === "priority"
497
+ ? priorityExtraPages
498
+ : 0,
474
499
  );
475
- }, [activeAccounts, isInboxView, resolvedInboxTab, sortMode]);
500
+ }, [
501
+ activeAccounts,
502
+ isInboxView,
503
+ showPrioritySort,
504
+ resolvedInboxTab,
505
+ sortMode,
506
+ ]);
476
507
  const inboxExtraOffsets = useMemo(
477
508
  () =>
478
509
  Array.from(
@@ -828,7 +859,7 @@ export function InboxPage() {
828
859
  activeAccounts.size > 0 ? Array.from(activeAccounts) : undefined,
829
860
  selectedThreadIds:
830
861
  selectedThreadIds.length > 0 ? selectedThreadIds : undefined,
831
- sort: sortMode === "priority" ? sortMode : undefined,
862
+ sort: sortMode === "priority" && showPrioritySort ? sortMode : undefined,
832
863
  });
833
864
  }, [
834
865
  view,
@@ -843,6 +874,8 @@ export function InboxPage() {
843
874
  activeInboxTab,
844
875
  activeAccounts,
845
876
  selectedThreadIds,
877
+ showPrioritySort,
878
+ jevAvailability.isError,
846
879
  sortMode,
847
880
  ]); // eslint-disable-line react-hooks/exhaustive-deps
848
881
 
@@ -851,6 +884,9 @@ export function InboxPage() {
851
884
  const lastCommandRef = useRef<string>("");
852
885
  useEffect(() => {
853
886
  if (!navCommand) return;
887
+ if (navCommand.sort === "priority" && jevAvailability.isLoading) {
888
+ return;
889
+ }
854
890
  const key = JSON.stringify(navCommand);
855
891
  if (key === lastCommandRef.current) return;
856
892
  lastCommandRef.current = key;
@@ -859,8 +895,12 @@ export function InboxPage() {
859
895
  const targetFilter = navCommand.filter;
860
896
  const targetThread = navCommand.threadId;
861
897
 
862
- if (navCommand.sort === "priority" || navCommand.sort === "newest") {
863
- setSortMode(navCommand.sort);
898
+ if (navCommand.sort === "newest") {
899
+ setSortMode("newest");
900
+ } else if (navCommand.sort === "priority") {
901
+ setSortMode(
902
+ jevAvailability.isError || jevConfigured ? "priority" : "newest",
903
+ );
864
904
  }
865
905
 
866
906
  if (navCommand.composeDraftId && !targetThread) {
@@ -898,7 +938,7 @@ export function InboxPage() {
898
938
 
899
939
  // Delete the command file so it doesn't re-trigger
900
940
  void navState.clearCommand();
901
- }, [navCommand, view, navigate]); // eslint-disable-line react-hooks/exhaustive-deps
941
+ }, [navCommand, view, navigate, jevAvailability.isLoading, jevConfigured]); // eslint-disable-line react-hooks/exhaustive-deps
902
942
  // Stable-identity pattern: keep the previous array reference when the
903
943
  // content hasn't meaningfully changed. Without this, markThreadRead's
904
944
  // optimistic update (which rebuilds the emails array for a single isRead
@@ -1117,6 +1157,7 @@ export function InboxPage() {
1117
1157
  isFetchingNextPage={isFetchingNextPage}
1118
1158
  isFetchNextPageError={isFetchNextPageError}
1119
1159
  sortMode={sortMode}
1160
+ showPrioritySort={showPrioritySort}
1120
1161
  onSortModeChange={setSortMode}
1121
1162
  />
1122
1163
  )}
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-09-22
4
+ ---
5
+
6
+ Jev-powered Mail rules and Priority sort work with an enabled Builder space or your personal Jev key.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-09-23
4
+ ---
5
+
6
+ Mail keeps Priority sort selected when Jev availability is temporarily unavailable.