@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +4 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +41 -12
- package/corpus/templates/analytics/app/lib/chat-handoff.ts +62 -0
- package/corpus/templates/analytics/changelog/2026-09-25-chat-sidebar-stays-closed-in-settings.md +6 -0
- package/corpus/templates/analytics/changelog/2026-09-25-show-one-recent-prompt-per-chat-turn.md +6 -0
- package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +74 -11
- package/corpus/templates/clips/app/i18n/en-US.ts +6 -0
- package/corpus/templates/clips/changelog/2026-09-25-one-click-builder-storage.md +6 -0
- package/corpus/templates/design/actions/get-design.schema.ts +19 -0
- package/corpus/templates/design/actions/get-design.ts +16 -19
- package/corpus/templates/mail/actions/apply-ai-filter.ts +8 -0
- package/corpus/templates/mail/actions/create-automation.ts +7 -1
- package/corpus/templates/mail/actions/manage-ai-filter-rule-undo.ts +34 -0
- package/corpus/templates/mail/actions/manage-automations.ts +21 -44
- package/corpus/templates/mail/app/components/email/EmailList.tsx +35 -0
- package/corpus/templates/mail/app/components/onboarding/AiInboxSetup.tsx +116 -34
- package/corpus/templates/mail/app/components/settings/AiFilterSection.tsx +108 -70
- package/corpus/templates/mail/app/components/settings/AiRulePromptField.tsx +8 -1
- package/corpus/templates/mail/app/components/settings/JevConnectionPrompt.tsx +171 -0
- package/corpus/templates/mail/app/hooks/use-automations.ts +24 -0
- package/corpus/templates/mail/app/i18n/en-US.ts +8 -2
- package/corpus/templates/mail/app/pages/InboxPage.tsx +12 -2
- package/corpus/templates/mail/changelog/2026-09-25-mail-now-guides-users-to-connect-jev-before-setting-up-triag.md +6 -0
- package/corpus/templates/mail/server/db/schema.ts +17 -0
- package/corpus/templates/mail/server/lib/ai-filter-rule-undo.ts +151 -0
- package/corpus/templates/mail/server/lib/automations.ts +34 -0
- package/corpus/templates/mail/server/plugins/db.ts +18 -0
- package/corpus/templates/mail/server/plugins/mail-jobs.ts +6 -0
- package/dist/client/AssistantChat.js +20 -3
- package/dist/client/BuilderReferralInviteRow.d.ts +3 -0
- package/dist/client/BuilderReferralInviteRow.js +16 -0
- package/dist/client/agent-chat-adapter.js +6 -5
- package/dist/client/chat/run-recovery.js +6 -5
- package/dist/client/settings/UsageSection.js +2 -1
- package/dist/client/settings/index.d.ts +1 -1
- package/dist/client/settings/index.js +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +4 -1
- package/dist/client/settings/useBuilderStatus.js +32 -16
- package/dist/client/use-agent-chat-running-threads.d.ts +2 -0
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/framework-tools.d.ts +3 -1
- package/dist/framework-tools.js +2 -0
- package/dist/localization/core-messages/ar-SA.js +4 -0
- package/dist/localization/core-messages/de-DE.js +4 -0
- package/dist/localization/core-messages/en-US.d.ts +4 -0
- package/dist/localization/core-messages/en-US.js +4 -0
- package/dist/localization/core-messages/es-ES.js +4 -0
- package/dist/localization/core-messages/fr-FR.js +4 -0
- package/dist/localization/core-messages/hi-IN.js +4 -0
- package/dist/localization/core-messages/ja-JP.js +4 -0
- package/dist/localization/core-messages/ko-KR.js +4 -0
- package/dist/localization/core-messages/pt-BR.js +4 -0
- package/dist/localization/core-messages/zh-CN.js +4 -0
- package/dist/localization/core-messages/zh-TW.js +4 -0
- package/dist/localization/core-messages.d.ts +4 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +14 -14
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/agent-chat/plugin-options.d.ts +1 -1
- package/dist/server/agent-chat-plugin.js +6 -4
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/dist/server/fusion-app.d.ts +3 -0
- package/dist/server/fusion-app.js +17 -0
- package/dist/shared/builder-referrals.d.ts +10 -0
- package/dist/shared/builder-referrals.js +29 -0
- package/dist/usage/actions/get-builder-referral-info.d.ts +9 -0
- package/dist/usage/actions/get-builder-referral-info.js +26 -0
- package/dist/usage/metrics-store.js +142 -30
- package/dist/vite/action-types-plugin.js +4 -0
- package/docs/content/actions-agent-tools.mdx +2 -1
- package/package.json +3 -2
package/corpus/README.md
CHANGED
|
@@ -16,6 +16,10 @@ time from the command menu (Cmd+K → "What's new") or from Settings.
|
|
|
16
16
|
- Human review previews Analytics dashboards as real charts
|
|
17
17
|
- Relevant metric definitions and saved dashboard examples are available with the first response so Analytics queries can reuse proven definitions and query shapes.
|
|
18
18
|
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Opening Settings no longer opens the chat sidebar.
|
|
22
|
+
|
|
19
23
|
## 2026-09-24
|
|
20
24
|
|
|
21
25
|
### Improved
|
|
@@ -18,13 +18,19 @@ import {
|
|
|
18
18
|
CreativeContextComposerChip,
|
|
19
19
|
useCreativeContextLab,
|
|
20
20
|
} from "@agent-native/creative-context/client";
|
|
21
|
-
import { useEffect, useMemo } from "react";
|
|
21
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
22
22
|
import { useLocation, useNavigate } from "react-router";
|
|
23
23
|
|
|
24
24
|
import { useNavigationState } from "@/hooks/use-navigation-state";
|
|
25
25
|
import {
|
|
26
26
|
ANALYTICS_CHAT_STORAGE_KEY,
|
|
27
|
+
ANALYTICS_RECENT_CHAT_HANDOFF_TTL_MS,
|
|
28
|
+
discardAnalyticsChatHandoffOnSettings,
|
|
29
|
+
hasTrackedAnalyticsChatRun,
|
|
30
|
+
isAnalyticsSettingsPath,
|
|
27
31
|
markAnalyticsChatActivity,
|
|
32
|
+
updateAnalyticsChatHandoffForRun,
|
|
33
|
+
type AnalyticsChatRunningRuns,
|
|
28
34
|
} from "@/lib/chat-handoff";
|
|
29
35
|
import { TAB_ID } from "@/lib/tab-id";
|
|
30
36
|
|
|
@@ -164,35 +170,58 @@ function InteractiveLayout({ children }: LayoutProps) {
|
|
|
164
170
|
location.pathname === "/monitoring" ||
|
|
165
171
|
location.pathname.startsWith("/monitoring/");
|
|
166
172
|
const isAskRoute = location.pathname === "/ask";
|
|
173
|
+
const isSettingsRoute = isAnalyticsSettingsPath(location.pathname);
|
|
174
|
+
const runningRuns = useRef<AnalyticsChatRunningRuns>(new Map());
|
|
167
175
|
const chatHomeHandoffActive = useAgentChatHomeHandoff({
|
|
168
176
|
storageKey: ANALYTICS_CHAT_STORAGE_KEY,
|
|
169
177
|
activePath: location.pathname,
|
|
170
|
-
|
|
178
|
+
ttlMs: ANALYTICS_RECENT_CHAT_HANDOFF_TTL_MS,
|
|
179
|
+
enabled: !isAskRoute && !isSettingsRoute,
|
|
171
180
|
});
|
|
172
|
-
const chatHomeHandoffPending =
|
|
173
|
-
|
|
174
|
-
|
|
181
|
+
const chatHomeHandoffPending =
|
|
182
|
+
!isSettingsRoute &&
|
|
183
|
+
isAgentChatHomeHandoffActive(ANALYTICS_CHAT_STORAGE_KEY, {
|
|
184
|
+
ttlMs: ANALYTICS_RECENT_CHAT_HANDOFF_TTL_MS,
|
|
185
|
+
});
|
|
175
186
|
useAgentChatHomeHandoffLinks({
|
|
176
187
|
storageKey: ANALYTICS_CHAT_STORAGE_KEY,
|
|
177
188
|
chatPath: "/ask",
|
|
189
|
+
ttlMs: ANALYTICS_RECENT_CHAT_HANDOFF_TTL_MS,
|
|
178
190
|
enabled: true,
|
|
179
191
|
requireActiveHandoff: true,
|
|
180
192
|
});
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
discardAnalyticsChatHandoffOnSettings(location.pathname);
|
|
195
|
+
}, [location.pathname]);
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (!isAskRoute) return;
|
|
198
|
+
const refreshHandoff = () => {
|
|
199
|
+
if (!hasTrackedAnalyticsChatRun(runningRuns.current)) return;
|
|
200
|
+
markAgentChatHomeHandoff(ANALYTICS_CHAT_STORAGE_KEY);
|
|
201
|
+
};
|
|
202
|
+
refreshHandoff();
|
|
203
|
+
const interval = window.setInterval(
|
|
204
|
+
refreshHandoff,
|
|
205
|
+
ANALYTICS_RECENT_CHAT_HANDOFF_TTL_MS / 2,
|
|
206
|
+
);
|
|
207
|
+
return () => window.clearInterval(interval);
|
|
208
|
+
}, [isAskRoute]);
|
|
181
209
|
useEffect(() => {
|
|
182
210
|
function handleChatRunning(event: Event) {
|
|
183
211
|
const detail = (event as CustomEvent).detail;
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
212
|
+
if (typeof detail?.isRunning !== "boolean") return;
|
|
213
|
+
if (location.pathname === "/ask") markAnalyticsChatActivity();
|
|
214
|
+
updateAnalyticsChatHandoffForRun(
|
|
215
|
+
runningRuns.current,
|
|
216
|
+
detail,
|
|
217
|
+
location.pathname,
|
|
218
|
+
);
|
|
190
219
|
}
|
|
191
220
|
|
|
192
221
|
window.addEventListener("agentNative.chatRunning", handleChatRunning);
|
|
193
222
|
return () =>
|
|
194
223
|
window.removeEventListener("agentNative.chatRunning", handleChatRunning);
|
|
195
|
-
}, [
|
|
224
|
+
}, [location.pathname]);
|
|
196
225
|
|
|
197
226
|
function openAskAgentFullscreen() {
|
|
198
227
|
focusAgentChat();
|
|
@@ -1,10 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
consumeAgentChatHomeHandoff,
|
|
3
|
+
markAgentChatHomeHandoff,
|
|
4
|
+
resolveAgentChatRunningThreadId,
|
|
5
|
+
type AgentChatRunningEventDetail,
|
|
6
|
+
} from "@agent-native/core/client/agent-chat";
|
|
7
|
+
|
|
1
8
|
export const ANALYTICS_CHAT_STORAGE_KEY = "analytics";
|
|
2
9
|
|
|
3
10
|
export const ANALYTICS_RECENT_CHAT_HANDOFF_TTL_MS = 5 * 60 * 1000;
|
|
4
11
|
|
|
12
|
+
export type AnalyticsChatRunningRuns = Map<string, Set<string>>;
|
|
13
|
+
|
|
5
14
|
const ANALYTICS_LAST_CHAT_ACTIVITY_KEY =
|
|
6
15
|
"agent-native.analytics.last-chat-activity-at";
|
|
7
16
|
|
|
17
|
+
export function isAnalyticsSettingsPath(pathname: string): boolean {
|
|
18
|
+
return pathname === "/settings" || pathname.startsWith("/settings/");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function discardAnalyticsChatHandoffOnSettings(pathname: string): void {
|
|
22
|
+
if (!isAnalyticsSettingsPath(pathname)) return;
|
|
23
|
+
consumeAgentChatHomeHandoff(ANALYTICS_CHAT_STORAGE_KEY, {
|
|
24
|
+
ttlMs: ANALYTICS_RECENT_CHAT_HANDOFF_TTL_MS,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function hasTrackedAnalyticsChatRun(
|
|
29
|
+
runningRuns: AnalyticsChatRunningRuns,
|
|
30
|
+
): boolean {
|
|
31
|
+
return Array.from(runningRuns.values()).some((runIds) => runIds.size > 0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function updateAnalyticsChatHandoffForRun(
|
|
35
|
+
runningRuns: AnalyticsChatRunningRuns,
|
|
36
|
+
detail: unknown,
|
|
37
|
+
pathname: string,
|
|
38
|
+
): void {
|
|
39
|
+
if (!detail || typeof detail !== "object") return;
|
|
40
|
+
const run = detail as Partial<AgentChatRunningEventDetail>;
|
|
41
|
+
if (typeof run.isRunning !== "boolean") return;
|
|
42
|
+
const tabId = resolveAgentChatRunningThreadId(run);
|
|
43
|
+
if (!tabId) return;
|
|
44
|
+
const runId =
|
|
45
|
+
(typeof run.runId === "string" && run.runId) ||
|
|
46
|
+
(typeof run.turnId === "string" && run.turnId) ||
|
|
47
|
+
null;
|
|
48
|
+
const state = runningRuns.get(tabId);
|
|
49
|
+
|
|
50
|
+
if (run.isRunning) {
|
|
51
|
+
if (pathname !== "/ask") return;
|
|
52
|
+
if (runId) {
|
|
53
|
+
const next = state ?? new Set<string>();
|
|
54
|
+
next.add(runId);
|
|
55
|
+
runningRuns.set(tabId, next);
|
|
56
|
+
}
|
|
57
|
+
markAgentChatHomeHandoff(ANALYTICS_CHAT_STORAGE_KEY);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!runId || !state) return;
|
|
62
|
+
const hadActiveRun = state.delete(runId);
|
|
63
|
+
|
|
64
|
+
if (state.size === 0) runningRuns.delete(tabId);
|
|
65
|
+
if (hadActiveRun && pathname === "/ask") {
|
|
66
|
+
markAgentChatHomeHandoff(ANALYTICS_CHAT_STORAGE_KEY);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
8
70
|
function readAnalyticsLastChatActivityAt(): number {
|
|
9
71
|
if (typeof window === "undefined") return 0;
|
|
10
72
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { agentNativePath, appPath } from "@agent-native/core/client/api-path";
|
|
2
2
|
import { useT } from "@agent-native/core/client/i18n";
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
hasBuilderOAuthCredential,
|
|
5
5
|
useBuilderConnectFlow,
|
|
6
6
|
} from "@agent-native/core/client/settings";
|
|
7
7
|
import {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@tabler/icons-react";
|
|
13
13
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
14
14
|
|
|
15
|
+
import { Button } from "@/components/ui/button";
|
|
15
16
|
import {
|
|
16
17
|
Tooltip,
|
|
17
18
|
TooltipContent,
|
|
@@ -168,6 +169,18 @@ export function StorageSetupCard({
|
|
|
168
169
|
},
|
|
169
170
|
[builderConnect.start],
|
|
170
171
|
);
|
|
172
|
+
const handleBuilderCancel = useCallback(() => {
|
|
173
|
+
connectRequestedRef.current = false;
|
|
174
|
+
builderConnect.cancel();
|
|
175
|
+
}, [builderConnect.cancel]);
|
|
176
|
+
const hasBuilderAccount =
|
|
177
|
+
builderConnect.accountExists || hasBuilderOAuthCredential(builderConnect);
|
|
178
|
+
const provisionAccount =
|
|
179
|
+
!hasBuilderAccount &&
|
|
180
|
+
builderConnect.statusResolved &&
|
|
181
|
+
builderConnect.agentNativeProvisioningEnabled;
|
|
182
|
+
const builderConnecting = builderConnect.connecting;
|
|
183
|
+
const actionConnecting = connecting || builderConnecting;
|
|
171
184
|
|
|
172
185
|
useEffect(() => {
|
|
173
186
|
mountedRef.current = true;
|
|
@@ -191,14 +204,13 @@ export function StorageSetupCard({
|
|
|
191
204
|
<p className="text-sm text-muted-foreground">{description}</p>
|
|
192
205
|
</div>
|
|
193
206
|
|
|
194
|
-
{/* Builder.io — primary option
|
|
195
|
-
<
|
|
196
|
-
flow={builderConnect}
|
|
197
|
-
onConnect={handleBuilderConnect}
|
|
198
|
-
>
|
|
207
|
+
{/* Builder.io — primary option. */}
|
|
208
|
+
<div className="space-y-2">
|
|
199
209
|
<button
|
|
200
210
|
type="button"
|
|
201
|
-
disabled={
|
|
211
|
+
disabled={actionConnecting || connected}
|
|
212
|
+
data-testid="storage-setup-builder-primary"
|
|
213
|
+
onClick={() => handleBuilderConnect(provisionAccount)}
|
|
202
214
|
className={
|
|
203
215
|
"flex items-start gap-3 rounded-xl border px-4 py-3.5 text-start transition-colors " +
|
|
204
216
|
(connected
|
|
@@ -216,7 +228,7 @@ export function StorageSetupCard({
|
|
|
216
228
|
>
|
|
217
229
|
{connected ? (
|
|
218
230
|
<IconCheck className="h-5 w-5" />
|
|
219
|
-
) :
|
|
231
|
+
) : actionConnecting ? (
|
|
220
232
|
<IconLoader2 className="h-5 w-5 animate-spin" />
|
|
221
233
|
) : (
|
|
222
234
|
<BuilderBMark className="h-5 w-5" />
|
|
@@ -227,9 +239,13 @@ export function StorageSetupCard({
|
|
|
227
239
|
<span className="text-sm font-medium">
|
|
228
240
|
{connected
|
|
229
241
|
? t("storageSetup.builderConnected")
|
|
230
|
-
:
|
|
242
|
+
: actionConnecting
|
|
231
243
|
? t("storageSetup.waitingForBuilder")
|
|
232
|
-
:
|
|
244
|
+
: provisionAccount
|
|
245
|
+
? t("storageSetup.createBuilderAccount")
|
|
246
|
+
: hasBuilderAccount
|
|
247
|
+
? t("storageSetup.signInWithBuilderAccount")
|
|
248
|
+
: t("storageSetup.connectBuilder")}
|
|
233
249
|
</span>
|
|
234
250
|
</div>
|
|
235
251
|
<span
|
|
@@ -244,7 +260,54 @@ export function StorageSetupCard({
|
|
|
244
260
|
</span>
|
|
245
261
|
</div>
|
|
246
262
|
</button>
|
|
247
|
-
|
|
263
|
+
{provisionAccount && (
|
|
264
|
+
<button
|
|
265
|
+
type="button"
|
|
266
|
+
disabled={actionConnecting || connected}
|
|
267
|
+
data-testid="storage-setup-builder-sign-in"
|
|
268
|
+
onClick={() => handleBuilderConnect(false)}
|
|
269
|
+
className="w-full text-center text-xs text-muted-foreground underline underline-offset-4 transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-60"
|
|
270
|
+
>
|
|
271
|
+
{t("storageSetup.signInWithBuilderAccount")}
|
|
272
|
+
</button>
|
|
273
|
+
)}
|
|
274
|
+
</div>
|
|
275
|
+
{builderConnecting && (
|
|
276
|
+
<Button
|
|
277
|
+
type="button"
|
|
278
|
+
variant="ghost"
|
|
279
|
+
size="sm"
|
|
280
|
+
data-testid="storage-setup-builder-cancel"
|
|
281
|
+
className="self-end text-xs font-normal text-muted-foreground"
|
|
282
|
+
onClick={handleBuilderCancel}
|
|
283
|
+
>
|
|
284
|
+
{t("common.cancel")}
|
|
285
|
+
</Button>
|
|
286
|
+
)}
|
|
287
|
+
|
|
288
|
+
{provisionAccount && (
|
|
289
|
+
<p className="text-center text-xs leading-5 text-muted-foreground">
|
|
290
|
+
{t("storageSetup.builderConsentPrefix")}{" "}
|
|
291
|
+
<a
|
|
292
|
+
href="https://www.builder.io/legal/terms"
|
|
293
|
+
target="_blank"
|
|
294
|
+
rel="noreferrer"
|
|
295
|
+
className="text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
296
|
+
>
|
|
297
|
+
{t("storageSetup.builderTerms")}
|
|
298
|
+
</a>{" "}
|
|
299
|
+
{t("storageSetup.builderConsentAnd")}{" "}
|
|
300
|
+
<a
|
|
301
|
+
href="https://www.builder.io/legal/privacy"
|
|
302
|
+
target="_blank"
|
|
303
|
+
rel="noreferrer"
|
|
304
|
+
className="text-foreground underline decoration-border underline-offset-2 hover:decoration-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
305
|
+
>
|
|
306
|
+
{t("storageSetup.builderPrivacy")}
|
|
307
|
+
</a>
|
|
308
|
+
.
|
|
309
|
+
</p>
|
|
310
|
+
)}
|
|
248
311
|
|
|
249
312
|
{err && <p className="text-xs text-muted-foreground">{err}</p>}
|
|
250
313
|
|
|
@@ -1357,6 +1357,12 @@ const messages = {
|
|
|
1357
1357
|
builderConnected: "Builder.io connected",
|
|
1358
1358
|
waitingForBuilder: "Waiting for Builder...",
|
|
1359
1359
|
connectBuilder: "Use Builder.io",
|
|
1360
|
+
createBuilderAccount: "Create Builder.io account",
|
|
1361
|
+
signInWithBuilderAccount: "Sign in with Builder.io account",
|
|
1362
|
+
builderConsentPrefix: "By creating a Builder.io account, you agree to our",
|
|
1363
|
+
builderTerms: "Terms of Service",
|
|
1364
|
+
builderConsentAnd: "and",
|
|
1365
|
+
builderPrivacy: "Privacy Policy",
|
|
1360
1366
|
free: "Free",
|
|
1361
1367
|
configureS3: "configure S3-compatible storage",
|
|
1362
1368
|
whyPrompt: "Why am I seeing this?",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
const queryBoolean = z.union([
|
|
4
|
+
z.boolean(),
|
|
5
|
+
z.enum(["true", "false", "1", "0"]),
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
export const getDesignSchema = z.object({
|
|
9
|
+
id: z.string().describe("Design ID"),
|
|
10
|
+
fileId: z.string().min(1).optional().describe("Read one design file by ID"),
|
|
11
|
+
includeFileContent: queryBoolean
|
|
12
|
+
.optional()
|
|
13
|
+
.transform((value) =>
|
|
14
|
+
value === undefined
|
|
15
|
+
? undefined
|
|
16
|
+
: value === true || value === "true" || value === "1",
|
|
17
|
+
)
|
|
18
|
+
.describe("Set false to return file metadata without HTML contents"),
|
|
19
|
+
});
|
|
@@ -3,12 +3,12 @@ import { loadAgentDesignSystemContext } from "@agent-native/core/shared";
|
|
|
3
3
|
import { resolveAccess } from "@agent-native/core/sharing";
|
|
4
4
|
import { track } from "@agent-native/core/tracking";
|
|
5
5
|
import { and, asc, eq } from "drizzle-orm";
|
|
6
|
-
import { z } from "zod";
|
|
7
6
|
|
|
8
7
|
import { getDb, schema } from "../server/db/index.js";
|
|
9
8
|
import { designDataForAccessRole } from "../server/lib/design-data-access.js";
|
|
10
9
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
11
10
|
import getDesignSystem from "./get-design-system.js";
|
|
11
|
+
import { getDesignSchema } from "./get-design.schema.js";
|
|
12
12
|
|
|
13
13
|
// The editor re-reads get-design after saves, on sync events, and every second
|
|
14
14
|
// while a generation runs. Count a signed-in viewer's view once per window,
|
|
@@ -40,14 +40,7 @@ function shouldTrackDesignView(
|
|
|
40
40
|
export default defineAction({
|
|
41
41
|
description:
|
|
42
42
|
"Get a design project by ID. Returns the full design data and linked `designSystem.agentContext` when readable. By default, returns all associated files; pass `includeFileContent=false` for file metadata only, then pass a `fileId` to read just one file. Treat design-system context as authoritative before authoring or restyling.",
|
|
43
|
-
schema:
|
|
44
|
-
id: z.string().describe("Design ID"),
|
|
45
|
-
fileId: z.string().min(1).optional().describe("Read one design file by ID"),
|
|
46
|
-
includeFileContent: z
|
|
47
|
-
.boolean()
|
|
48
|
-
.optional()
|
|
49
|
-
.describe("Set false to return file metadata without HTML contents"),
|
|
50
|
-
}),
|
|
43
|
+
schema: getDesignSchema,
|
|
51
44
|
readOnly: true,
|
|
52
45
|
requiresAuth: false,
|
|
53
46
|
publicAgent: { expose: true, readOnly: true, requiresAuth: false },
|
|
@@ -134,16 +127,20 @@ export default defineAction({
|
|
|
134
127
|
createdAt: row.createdAt,
|
|
135
128
|
updatedAt: row.updatedAt,
|
|
136
129
|
accessRole: access.role,
|
|
137
|
-
files: files.map((f) =>
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
130
|
+
files: files.map((f) => {
|
|
131
|
+
const metadata = {
|
|
132
|
+
id: f.id,
|
|
133
|
+
filename: f.filename,
|
|
134
|
+
fileType: f.fileType,
|
|
135
|
+
createdAt: f.createdAt,
|
|
136
|
+
updatedAt: f.updatedAt,
|
|
137
|
+
};
|
|
138
|
+
if (includeFileContent === false) return metadata;
|
|
139
|
+
if (!("content" in f)) {
|
|
140
|
+
throw new Error("File content was requested but not selected");
|
|
141
|
+
}
|
|
142
|
+
return { ...metadata, content: f.content };
|
|
143
|
+
}),
|
|
147
144
|
};
|
|
148
145
|
},
|
|
149
146
|
});
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
ensureGmailLabel,
|
|
23
23
|
} from "../server/lib/automation-actions.js";
|
|
24
24
|
import {
|
|
25
|
+
assertMailJevEnabled,
|
|
25
26
|
createAutomationRule,
|
|
26
27
|
listAutomationRules,
|
|
27
28
|
} from "../server/lib/automations.js";
|
|
@@ -144,6 +145,12 @@ export default defineAction({
|
|
|
144
145
|
if (!ownerEmail) throw new Error("no authenticated user");
|
|
145
146
|
|
|
146
147
|
if (args.mode === "settings") {
|
|
148
|
+
const settingKeys = Object.keys(args.settings ?? {});
|
|
149
|
+
const disableOnly =
|
|
150
|
+
settingKeys.length === 1 && args.settings?.enabled === false;
|
|
151
|
+
if (settingKeys.length > 0 && !disableOnly) {
|
|
152
|
+
await assertMailJevEnabled(ownerEmail);
|
|
153
|
+
}
|
|
147
154
|
const state = await getAiFilterState(ownerEmail);
|
|
148
155
|
const next = {
|
|
149
156
|
...state,
|
|
@@ -154,6 +161,7 @@ export default defineAction({
|
|
|
154
161
|
return { changed: 0, failures: [], state: next };
|
|
155
162
|
}
|
|
156
163
|
|
|
164
|
+
await assertMailJevEnabled(ownerEmail);
|
|
157
165
|
const targets = args.targets ?? [];
|
|
158
166
|
if (targets.length === 0) throw new Error("targets are required");
|
|
159
167
|
|
|
@@ -3,7 +3,10 @@ import { getRequestUserEmail } from "@agent-native/core/server";
|
|
|
3
3
|
import { track } from "@agent-native/core/tracking";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
assertMailJevEnabled,
|
|
8
|
+
createAutomationRule,
|
|
9
|
+
} from "../server/lib/automations.js";
|
|
7
10
|
import { automationActionSchema } from "../shared/automation-schema.js";
|
|
8
11
|
|
|
9
12
|
export default defineAction({
|
|
@@ -25,6 +28,9 @@ export default defineAction({
|
|
|
25
28
|
if (!args.name || !args.condition || !args.actions) {
|
|
26
29
|
throw new Error("name, condition, and actions are required");
|
|
27
30
|
}
|
|
31
|
+
if ((args.domain ?? "mail") === "mail" && args.kind === "ai-filter") {
|
|
32
|
+
await assertMailJevEnabled(ownerEmail);
|
|
33
|
+
}
|
|
28
34
|
const result = await createAutomationRule(ownerEmail, args);
|
|
29
35
|
track(
|
|
30
36
|
"automation_created",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { getRequestUserEmail } from "@agent-native/core/server";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
clearMailAiFilterRules,
|
|
7
|
+
restoreMailAiFilterRules,
|
|
8
|
+
} from "../server/lib/ai-filter-rule-undo.js";
|
|
9
|
+
|
|
10
|
+
export default defineAction({
|
|
11
|
+
description:
|
|
12
|
+
"Clear active Mail AI-filter rules or restore them from a short-lived undo token.",
|
|
13
|
+
schema: z.discriminatedUnion("operation", [
|
|
14
|
+
z.object({
|
|
15
|
+
operation: z.literal("clear"),
|
|
16
|
+
ids: z.array(z.string().min(1)).min(1).max(32),
|
|
17
|
+
}),
|
|
18
|
+
z.object({
|
|
19
|
+
operation: z.literal("undo"),
|
|
20
|
+
undoId: z.string().min(1),
|
|
21
|
+
}),
|
|
22
|
+
]),
|
|
23
|
+
agentTool: false,
|
|
24
|
+
run: async (args) => {
|
|
25
|
+
const ownerEmail = getRequestUserEmail();
|
|
26
|
+
if (!ownerEmail) throw new Error("Unauthenticated");
|
|
27
|
+
if (args.operation === "clear") {
|
|
28
|
+
const undoId = await clearMailAiFilterRules(ownerEmail, args.ids);
|
|
29
|
+
return { undoId };
|
|
30
|
+
}
|
|
31
|
+
await restoreMailAiFilterRules(ownerEmail, args.undoId);
|
|
32
|
+
return { restored: true };
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -4,6 +4,8 @@ import { eq, and } from "drizzle-orm";
|
|
|
4
4
|
import { nanoid } from "nanoid";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
|
|
7
|
+
import type { AutomationAction } from "../shared/types.js";
|
|
8
|
+
|
|
7
9
|
export const createManageEmailRulesAction = (agentTool: boolean) =>
|
|
8
10
|
defineAction({
|
|
9
11
|
description:
|
|
@@ -103,31 +105,26 @@ export const createManageEmailRulesAction = (agentTool: boolean) =>
|
|
|
103
105
|
case "update": {
|
|
104
106
|
if (!args.id) throw new Error("--id is required for update");
|
|
105
107
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
const patch: {
|
|
109
|
+
name?: string;
|
|
110
|
+
condition?: string;
|
|
111
|
+
actions?: AutomationAction[];
|
|
112
|
+
enabled?: boolean;
|
|
113
|
+
} = {};
|
|
114
|
+
if (args.name !== undefined) patch.name = args.name;
|
|
115
|
+
if (args.condition !== undefined) patch.condition = args.condition;
|
|
111
116
|
if (args.actions !== undefined) {
|
|
112
117
|
try {
|
|
113
|
-
JSON.parse(args.actions);
|
|
114
|
-
updates.actions = args.actions;
|
|
118
|
+
patch.actions = JSON.parse(args.actions);
|
|
115
119
|
} catch {
|
|
116
120
|
throw new Error("--actions must be valid JSON array");
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
|
-
if (args.enabled !== undefined)
|
|
120
|
-
updates.enabled = args.enabled ? 1 : 0;
|
|
123
|
+
if (args.enabled !== undefined) patch.enabled = args.enabled;
|
|
121
124
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
.where(
|
|
126
|
-
and(
|
|
127
|
-
eq(schema.automationRules.id, args.id),
|
|
128
|
-
eq(schema.automationRules.ownerEmail, ownerEmail),
|
|
129
|
-
),
|
|
130
|
-
);
|
|
125
|
+
const { updateAutomationRule } =
|
|
126
|
+
await import("../server/lib/automations.js");
|
|
127
|
+
await updateAutomationRule(ownerEmail, args.id, patch);
|
|
131
128
|
|
|
132
129
|
return `Updated automation rule ${args.id}`;
|
|
133
130
|
}
|
|
@@ -149,38 +146,18 @@ export const createManageEmailRulesAction = (agentTool: boolean) =>
|
|
|
149
146
|
|
|
150
147
|
case "enable": {
|
|
151
148
|
if (!args.id) throw new Error("--id is required for enable");
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
.set({
|
|
156
|
-
enabled: 1,
|
|
157
|
-
updatedAt: Math.floor(Date.now() / 1_000),
|
|
158
|
-
} as any)
|
|
159
|
-
.where(
|
|
160
|
-
and(
|
|
161
|
-
eq(schema.automationRules.id, args.id),
|
|
162
|
-
eq(schema.automationRules.ownerEmail, ownerEmail),
|
|
163
|
-
),
|
|
164
|
-
);
|
|
149
|
+
const { updateAutomationRule } =
|
|
150
|
+
await import("../server/lib/automations.js");
|
|
151
|
+
await updateAutomationRule(ownerEmail, args.id, { enabled: true });
|
|
165
152
|
|
|
166
153
|
return `Enabled automation rule ${args.id}`;
|
|
167
154
|
}
|
|
168
155
|
|
|
169
156
|
case "disable": {
|
|
170
157
|
if (!args.id) throw new Error("--id is required for disable");
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
.set({
|
|
175
|
-
enabled: 0,
|
|
176
|
-
updatedAt: Math.floor(Date.now() / 1_000),
|
|
177
|
-
} as any)
|
|
178
|
-
.where(
|
|
179
|
-
and(
|
|
180
|
-
eq(schema.automationRules.id, args.id),
|
|
181
|
-
eq(schema.automationRules.ownerEmail, ownerEmail),
|
|
182
|
-
),
|
|
183
|
-
);
|
|
158
|
+
const { updateAutomationRule } =
|
|
159
|
+
await import("../server/lib/automations.js");
|
|
160
|
+
await updateAutomationRule(ownerEmail, args.id, { enabled: false });
|
|
184
161
|
|
|
185
162
|
return `Disabled automation rule ${args.id}`;
|
|
186
163
|
}
|