@agent-native/core 0.145.8 → 0.146.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/analytics/actions/compose-dashboard.ts +2 -27
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +18 -3
- package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +75 -24
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +16 -24
- package/corpus/templates/analytics/actions/rename-dashboard.ts +2 -24
- package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +2 -24
- package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +2 -25
- package/corpus/templates/analytics/actions/update-dashboard.ts +4 -32
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +1 -40
- package/corpus/templates/analytics/changelog/2026-08-07-analytics-migrations-now-use-bounded-pressure-aware-backgrou.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-07-language-selection-now-lives-in-settings-instead-of-the-comm.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-07-template-activity-charts-now-show-only-core-agent-native-app.md +6 -0
- package/corpus/templates/analytics/netlify.toml +7 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +110 -0
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +450 -0
- package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +2 -2
- package/corpus/templates/analytics/server/lib/analytics-rollup-lock.ts +4 -5
- package/corpus/templates/analytics/server/lib/dashboard-collab-sync.ts +77 -0
- package/corpus/templates/analytics/server/lib/demo-dashboards.ts +2 -23
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +10 -4
- package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +4 -11
- package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +2 -5
- package/corpus/templates/analytics/server/plugins/db.ts +42 -0
- package/corpus/templates/analytics/server/routes/api/analytics-bigquery/backfill.post.ts +55 -0
- package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +18 -4
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/brain/server/lib/connectors.ts +38 -14
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/clips/app/components/player/video-player.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-08-07-fixed-trimmed-clips-durations-so-the-main-player-display-mat.md +6 -0
- package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +1 -1
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/dispatch/changelog/2026-08-07-language-selection-now-lives-in-settings-instead-of-the-disp.md +6 -0
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +59 -22
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +10 -1
- package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +10 -3
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/corpus/templates/slides/actions/generate-image-api.ts +123 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +10 -2
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +13 -0
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -0
- package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +1 -1
- package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +5 -0
- package/corpus/templates/slides/app/components/presentation/PresenterView.tsx +5 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +3 -0
- package/corpus/templates/slides/app/pages/Presentation.tsx +3 -0
- package/corpus/templates/slides/app/pages/SharedPresentation.tsx +5 -0
- package/corpus/templates/slides/app/routes/slide.tsx +10 -5
- package/corpus/templates/slides/changelog/2026-08-07-deleting-a-deck-no-longer-freezes-the-slides-browser.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-07-slide-previews-and-present-now-keep-deck-colors-consistent.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-07-slides-verify-generated-images-are-persisted-on-the-target-s.md +6 -0
- package/corpus/templates/slides/server/db/schema.ts +1 -0
- package/corpus/templates/slides/server/handlers/share.ts +51 -3
- package/corpus/templates/slides/server/lib/slide-image-insertion.ts +141 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/slides/server/plugins/db.ts +5 -0
- package/corpus/templates/slides/shared/api.ts +2 -0
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/dist/cli/code-agent-executor.js +8 -3
- package/dist/client/AgentPanel.d.ts +10 -4
- package/dist/client/AgentPanel.js +29 -82
- package/dist/client/ConnectBuilderCard.d.ts +7 -7
- package/dist/client/ConnectBuilderCard.js +45 -20
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/config.d.ts +9 -2
- package/dist/config.js +38 -2
- package/dist/db/client.d.ts +13 -1
- package/dist/db/client.js +76 -2
- package/dist/db/create-get-db.js +2 -2
- package/dist/db/migrations.js +22 -4
- package/dist/deploy/build.d.ts +1 -0
- package/dist/deploy/build.js +32 -23
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/scripts/docs/search.js +3 -2
- package/dist/server/agent-chat/prompt-resources.js +4 -3
- package/dist/server/agents-bundle.d.ts +20 -3
- package/dist/server/agents-bundle.js +57 -30
- package/dist/server/better-auth-instance.js +2 -2
- package/dist/server/builder-browser.d.ts +34 -0
- package/dist/server/builder-browser.js +179 -0
- package/dist/server/core-routes-plugin.d.ts +5 -0
- package/dist/server/core-routes-plugin.js +24 -8
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/shared/cache-control.d.ts +2 -2
- package/dist/shared/cache-control.js +2 -2
- package/dist/shared/runtime-config.js +1 -1
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/dist/vite/agent-native-config-loader.d.ts +10 -0
- package/dist/vite/agent-native-config-loader.js +56 -0
- package/dist/vite/agents-bundle-plugin.d.ts +5 -1
- package/dist/vite/agents-bundle-plugin.js +29 -6
- package/dist/vite/client.d.ts +4 -2
- package/dist/vite/client.js +17 -50
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite/index.js +1 -1
- package/docs/content/agent-native-config.mdx +43 -17
- package/docs/content/drop-in-agent.mdx +3 -3
- package/docs/content/environment-variables.mdx +1 -1
- package/docs/content/getting-started-actions.mdx +1 -2
- package/docs/content/getting-started-pages.mdx +1 -2
- package/docs/content/getting-started.mdx +0 -4
- package/docs/content/what-is-agent-native.mdx +4 -4
- package/package.json +1 -1
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +41 -11
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +21 -2
- package/docs/content/getting-started-database.mdx +0 -173
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { IconExternalLink, IconLoader2 } from "@tabler/icons-react";
|
|
2
|
+
import { IconCheck, IconCode, IconCopy, IconExternalLink, IconLoader2, } from "@tabler/icons-react";
|
|
3
3
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { withBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";
|
|
5
5
|
import { agentNativePath } from "./api-path.js";
|
|
6
6
|
import { BuilderBMark } from "./builder-mark.js";
|
|
7
|
+
import { writeClipboardText } from "./clipboard.js";
|
|
7
8
|
import { getCallbackOrigin } from "./frame.js";
|
|
8
9
|
import { useBuilderConnectFlow } from "./settings/useBuilderStatus.js";
|
|
9
10
|
import { cn } from "./utils.js";
|
|
10
11
|
const DESKTOP_DOWNLOAD_URL = "https://www.agent-native.com/download";
|
|
11
12
|
const CODE_CHANGE_FALLBACK_DETAIL = "Edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way you like.";
|
|
12
13
|
const CODE_CHANGE_FALLBACK_TEXT = `This requires a code change. ${CODE_CHANGE_FALLBACK_DETAIL}`;
|
|
13
|
-
function
|
|
14
|
-
if (typeof window === "undefined"
|
|
14
|
+
function isLocalDevelopment() {
|
|
15
|
+
if (typeof window === "undefined") {
|
|
15
16
|
return false;
|
|
16
17
|
}
|
|
17
18
|
const hostname = window.location.hostname;
|
|
18
|
-
|
|
19
|
-
return local && !/AgentNativeDesktop/i.test(navigator.userAgent || "");
|
|
19
|
+
return (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1");
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Rich inline card rendered for the `connect-builder` tool call. Shows
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* Rich inline card rendered for the `connect-builder` tool call. Shows the
|
|
23
|
+
* Builder handoff when available, or routes local development requests to the
|
|
24
|
+
* external coding agent.
|
|
25
25
|
*/
|
|
26
26
|
export function ConnectBuilderCard({ configured: initialConfigured, builderEnabled: initialBuilderEnabled = true, connectUrl: initialConnectUrl, orgName: initialOrgName, prompt = "", }) {
|
|
27
27
|
// The connect-poll state machine is shared — the tool-call result is
|
|
@@ -46,8 +46,11 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
|
|
|
46
46
|
const [sending, setSending] = useState(false);
|
|
47
47
|
const [runResult, setRunResult] = useState(null);
|
|
48
48
|
const [sendErr, setSendErr] = useState(null);
|
|
49
|
-
const [
|
|
49
|
+
const [copyErr, setCopyErr] = useState(null);
|
|
50
|
+
const [promptCopied, setPromptCopied] = useState(false);
|
|
51
|
+
const [localDevelopment] = useState(() => isLocalDevelopment());
|
|
50
52
|
const mountedRef = useRef(true);
|
|
53
|
+
const copyResetRef = useRef(null);
|
|
51
54
|
// Tracks whether the user clicked "Connect Builder" *this session*. When
|
|
52
55
|
// the connect-then-poll round-trip lands `configured=true`, we use this
|
|
53
56
|
// flag to decide whether to retry the user's pending prompt automatically
|
|
@@ -58,9 +61,10 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
|
|
|
58
61
|
const wasConnectingRef = useRef(false);
|
|
59
62
|
useEffect(() => {
|
|
60
63
|
mountedRef.current = true;
|
|
61
|
-
setLocalBrowser(isLocalBrowserOutsideDesktop());
|
|
62
64
|
return () => {
|
|
63
65
|
mountedRef.current = false;
|
|
66
|
+
if (copyResetRef.current)
|
|
67
|
+
clearTimeout(copyResetRef.current);
|
|
64
68
|
};
|
|
65
69
|
}, []);
|
|
66
70
|
const handleSend = useCallback(async () => {
|
|
@@ -127,8 +131,27 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
|
|
|
127
131
|
setJoiningWaitlist(false);
|
|
128
132
|
}
|
|
129
133
|
}, [orgName, prompt]);
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
const handleCopyPrompt = useCallback(async () => {
|
|
135
|
+
if (!prompt.trim())
|
|
136
|
+
return;
|
|
137
|
+
setCopyErr(null);
|
|
138
|
+
const copied = await writeClipboardText(prompt);
|
|
139
|
+
if (!mountedRef.current)
|
|
140
|
+
return;
|
|
141
|
+
if (!copied) {
|
|
142
|
+
setCopyErr("Couldn't copy the prompt");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
setPromptCopied(true);
|
|
146
|
+
if (copyResetRef.current)
|
|
147
|
+
clearTimeout(copyResetRef.current);
|
|
148
|
+
copyResetRef.current = setTimeout(() => {
|
|
149
|
+
if (mountedRef.current)
|
|
150
|
+
setPromptCopied(false);
|
|
151
|
+
}, 1600);
|
|
152
|
+
}, [prompt]);
|
|
153
|
+
// Combine connect-flow errors, send errors, waitlist errors, and copy errors.
|
|
154
|
+
const err = sendErr ?? waitlistErr ?? copyErr ?? flow.error;
|
|
132
155
|
const hasPrompt = prompt.trim().length > 0;
|
|
133
156
|
const canSend = configured && builderEnabled && hasPrompt;
|
|
134
157
|
// Auto-send the user's pending prompt the moment connecting finishes
|
|
@@ -149,7 +172,8 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
|
|
|
149
172
|
}, [flow.connecting, canSend, sending, runResult, sendErr, handleSend]);
|
|
150
173
|
// Branch creation is gated by a server-side project id, which may come
|
|
151
174
|
// from deployment config or org-scoped secrets.
|
|
152
|
-
const
|
|
175
|
+
const showExternalAgentHandoff = localDevelopment && !builderEnabled && hasPrompt;
|
|
176
|
+
const showWaitlist = !localDevelopment && !builderEnabled && hasPrompt;
|
|
153
177
|
// Title + subtitle depend on which mode we're in. We compute them up front
|
|
154
178
|
// so the render tree below stays flat.
|
|
155
179
|
const connectedCapabilityText = builderEnabled
|
|
@@ -161,13 +185,14 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
|
|
|
161
185
|
title = "Builder is working on it";
|
|
162
186
|
subtitle = (_jsxs(_Fragment, { children: ["Working on branch", " ", _jsx("span", { className: "font-mono text-foreground", children: runResult.branchName }), ". Click through to watch progress in the Visual Editor."] }));
|
|
163
187
|
}
|
|
188
|
+
else if (showExternalAgentHandoff) {
|
|
189
|
+
title = "This requires a code change";
|
|
190
|
+
subtitle =
|
|
191
|
+
"Open your coding agent in this project, then paste this request.";
|
|
192
|
+
}
|
|
164
193
|
else if (showWaitlist) {
|
|
165
194
|
title = "This requires a code change";
|
|
166
|
-
subtitle = waitlistJoined ? (_jsxs(_Fragment, { children: ["You're on the waitlist. ", CODE_CHANGE_FALLBACK_DETAIL, " ",
|
|
167
|
-
? "Since this project is already running locally, open it in the desktop app for local coding tools or keep editing from your clone."
|
|
168
|
-
: "You can still clone the project locally and use the desktop app for code changes."] })) : (_jsxs(_Fragment, { children: [CODE_CHANGE_FALLBACK_DETAIL, " ", localBrowser
|
|
169
|
-
? "Since this project is already running locally, open it in the desktop app for local coding tools or keep editing from your clone."
|
|
170
|
-
: "You can still clone the project locally and use the desktop app for code changes."] }));
|
|
195
|
+
subtitle = waitlistJoined ? (_jsxs(_Fragment, { children: ["You're on the waitlist. ", CODE_CHANGE_FALLBACK_DETAIL, " You can still clone the project locally and use the desktop app for code changes."] })) : (_jsxs(_Fragment, { children: [CODE_CHANGE_FALLBACK_DETAIL, " You can still clone the project locally and use the desktop app for code changes."] }));
|
|
171
196
|
}
|
|
172
197
|
else if (canSend) {
|
|
173
198
|
title = "Send this to Builder";
|
|
@@ -181,8 +206,8 @@ export function ConnectBuilderCard({ configured: initialConfigured, builderEnabl
|
|
|
181
206
|
title = "Connect Builder.io";
|
|
182
207
|
subtitle = _jsx(_Fragment, { children: "Builder.io's free tier includes AI credits." });
|
|
183
208
|
}
|
|
184
|
-
return (_jsx("div", { className: cn("my-2 rounded-lg border border-border overflow-hidden"), children: _jsxs("div", { className: "flex items-start gap-3 px-4 py-3.5 bg-gradient-to-br from-teal-500/5 via-transparent to-transparent", children: [_jsx("div", { className: cn("flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", "bg-foreground text-background"), children: runResult ? (_jsx(IconLoader2, { className: "h-5 w-5 animate-spin" })) : (_jsx(BuilderBMark, { className: "h-5 w-5" })) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "flex items-center gap-2 flex-wrap", children: _jsx("span", { className: "text-sm font-semibold text-foreground", children: title }) }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground leading-relaxed", children: subtitle }), showWaitlist && (_jsxs("a", { href: DESKTOP_DOWNLOAD_URL, target: "_blank", rel: "noopener noreferrer", className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground no-underline hover:text-foreground", children: ["Download desktop app", _jsx(IconExternalLink, { className: "h-3 w-3" })] })), err && _jsx("div", { className: "mt-2 text-xs text-destructive", children: err }), _jsx("div", { className: "mt-3", children: runResult ? (_jsxs("a", { href: withBuilderUtmTrackingParams(runResult.url, {
|
|
209
|
+
return (_jsx("div", { className: cn("my-2 rounded-lg border border-border overflow-hidden"), children: _jsxs("div", { className: "flex items-start gap-3 px-4 py-3.5 bg-gradient-to-br from-teal-500/5 via-transparent to-transparent", children: [_jsx("div", { className: cn("flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", "bg-foreground text-background"), children: runResult ? (_jsx(IconLoader2, { className: "h-5 w-5 animate-spin" })) : showExternalAgentHandoff ? (_jsx(IconCode, { className: "h-5 w-5" })) : (_jsx(BuilderBMark, { className: "h-5 w-5" })) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "flex items-center gap-2 flex-wrap", children: _jsx("span", { className: "text-sm font-semibold text-foreground", children: title }) }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground leading-relaxed", children: subtitle }), showWaitlist && (_jsxs("a", { href: DESKTOP_DOWNLOAD_URL, target: "_blank", rel: "noopener noreferrer", className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground no-underline hover:text-foreground", children: ["Download desktop app", _jsx(IconExternalLink, { className: "h-3 w-3" })] })), err && _jsx("div", { className: "mt-2 text-xs text-destructive", children: err }), _jsx("div", { className: "mt-3", children: runResult ? (_jsxs("a", { href: withBuilderUtmTrackingParams(runResult.url, {
|
|
185
210
|
campaign: "product",
|
|
186
211
|
content: "connect_builder_card_branch",
|
|
187
|
-
}), target: "_blank", rel: "noopener noreferrer", className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: ["Open branch in Builder", _jsx(IconExternalLink, { className: "h-3.5 w-3.5" })] })) : canSend ? (_jsx("button", { type: "button", onClick: handleSend, disabled: sending, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", sending && "opacity-70 cursor-wait"), children: sending ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Sending to Builder\u2026"] })) : (_jsx(_Fragment, { children: "Send to Builder" })) })) : showWaitlist && !waitlistJoined ? (_jsx("button", { type: "button", onClick: handleJoinWaitlist, disabled: joiningWaitlist, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", joiningWaitlist && "opacity-70 cursor-wait"), children: joiningWaitlist ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Joining\u2026"] })) : (_jsx(_Fragment, { children: "Join the waitlist" })) })) : !configured ? (_jsx("button", { type: "button", onClick: () => flow.start(), disabled: connecting, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", connecting && "opacity-70 cursor-wait"), children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Waiting for Builder\u2026"] })) : ("Connect Builder") })) : null })] })] }) }));
|
|
212
|
+
}), target: "_blank", rel: "noopener noreferrer", className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: ["Open branch in Builder", _jsx(IconExternalLink, { className: "h-3.5 w-3.5" })] })) : canSend ? (_jsx("button", { type: "button", onClick: handleSend, disabled: sending, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", sending && "opacity-70 cursor-wait"), children: sending ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Sending to Builder\u2026"] })) : (_jsx(_Fragment, { children: "Send to Builder" })) })) : showExternalAgentHandoff ? (_jsx("button", { type: "button", onClick: () => void handleCopyPrompt(), className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: promptCopied ? (_jsxs(_Fragment, { children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), "Prompt copied"] })) : (_jsxs(_Fragment, { children: [_jsx(IconCopy, { className: "h-3.5 w-3.5" }), "Copy prompt"] })) })) : showWaitlist && !waitlistJoined ? (_jsx("button", { type: "button", onClick: handleJoinWaitlist, disabled: joiningWaitlist, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", joiningWaitlist && "opacity-70 cursor-wait"), children: joiningWaitlist ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Joining\u2026"] })) : (_jsx(_Fragment, { children: "Join the waitlist" })) })) : !configured ? (_jsx("button", { type: "button", onClick: () => flow.start(), disabled: connecting, className: cn("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90", connecting && "opacity-70 cursor-wait"), children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "h-3.5 w-3.5 animate-spin" }), "Waiting for Builder\u2026"] })) : ("Connect Builder") })) : null })] })] }) }));
|
|
188
213
|
}
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
-
error?: undefined;
|
|
66
65
|
states: {
|
|
67
66
|
clientId: number;
|
|
68
67
|
state: string;
|
|
69
68
|
}[];
|
|
69
|
+
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
-
error?: undefined;
|
|
81
80
|
users: {
|
|
82
81
|
clientId: number;
|
|
83
82
|
lastSeen: number;
|
|
84
83
|
}[];
|
|
84
|
+
error?: undefined;
|
|
85
85
|
}>>;
|
package/dist/config.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Public, non-secret configuration for an Agent-Native app.
|
|
3
3
|
*
|
|
4
4
|
* This module is intentionally free of Node and framework imports so it can be
|
|
5
|
-
* used from a typed `agent-native.ts` file and from browser code after Vite
|
|
5
|
+
* used from a typed `agent-native.config.ts` file and from browser code after Vite
|
|
6
6
|
* serializes the resolved config into the client bundle.
|
|
7
7
|
*/
|
|
8
8
|
export declare const AGENT_NATIVE_CONFIG_VERSION: 1;
|
|
@@ -42,11 +42,18 @@ export interface AgentNativeDiagnosticsConfig {
|
|
|
42
42
|
/** Fail a production Vite build when runtime configuration has issues. */
|
|
43
43
|
failOnBuild?: boolean;
|
|
44
44
|
}
|
|
45
|
+
export interface AgentNativeInstructionsConfig {
|
|
46
|
+
/** Relative Markdown file loaded by the in-app runtime agent. */
|
|
47
|
+
runtime?: string;
|
|
48
|
+
/** Relative Markdown file loaded by development/coding agents. */
|
|
49
|
+
development?: string;
|
|
50
|
+
}
|
|
45
51
|
export interface AgentNativeConfig {
|
|
46
52
|
version?: typeof AGENT_NATIVE_CONFIG_VERSION;
|
|
47
53
|
onboarding?: AgentNativeOnboardingConfig;
|
|
48
54
|
runtime?: AgentNativeRuntimeConfig;
|
|
49
55
|
diagnostics?: AgentNativeDiagnosticsConfig;
|
|
56
|
+
instructions?: AgentNativeInstructionsConfig;
|
|
50
57
|
}
|
|
51
58
|
export interface AgentNativeConfigContext {
|
|
52
59
|
command: "serve" | "build";
|
|
@@ -57,7 +64,7 @@ export interface AgentNativeConfigContext {
|
|
|
57
64
|
export type AgentNativeConfigFactory = (context: AgentNativeConfigContext) => AgentNativeConfig;
|
|
58
65
|
export type AgentNativeConfigInput = AgentNativeConfig | AgentNativeConfigFactory;
|
|
59
66
|
/**
|
|
60
|
-
* Type-safe authoring helper for `agent-native.ts`.
|
|
67
|
+
* Type-safe authoring helper for `agent-native.config.ts`.
|
|
61
68
|
*
|
|
62
69
|
* Like Next's typed config file, this is deliberately identity-like: the
|
|
63
70
|
* framework evaluates the exported object or factory in the Vite config
|
package/dist/config.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Public, non-secret configuration for an Agent-Native app.
|
|
3
3
|
*
|
|
4
4
|
* This module is intentionally free of Node and framework imports so it can be
|
|
5
|
-
* used from a typed `agent-native.ts` file and from browser code after Vite
|
|
5
|
+
* used from a typed `agent-native.config.ts` file and from browser code after Vite
|
|
6
6
|
* serializes the resolved config into the client bundle.
|
|
7
7
|
*/
|
|
8
8
|
export const AGENT_NATIVE_CONFIG_VERSION = 1;
|
|
9
9
|
/**
|
|
10
|
-
* Type-safe authoring helper for `agent-native.ts`.
|
|
10
|
+
* Type-safe authoring helper for `agent-native.config.ts`.
|
|
11
11
|
*
|
|
12
12
|
* Like Next's typed config file, this is deliberately identity-like: the
|
|
13
13
|
* framework evaluates the exported object or factory in the Vite config
|
|
@@ -27,6 +27,7 @@ export function normalizeAgentNativeConfig(input, source = "agent-native config"
|
|
|
27
27
|
const onboardingValue = input.onboarding;
|
|
28
28
|
const runtimeValue = input.runtime;
|
|
29
29
|
const diagnosticsValue = input.diagnostics;
|
|
30
|
+
const instructionsValue = input.instructions;
|
|
30
31
|
const normalized = {
|
|
31
32
|
...(input.version === undefined
|
|
32
33
|
? {}
|
|
@@ -45,6 +46,9 @@ export function normalizeAgentNativeConfig(input, source = "agent-native config"
|
|
|
45
46
|
if (diagnosticsValue !== undefined) {
|
|
46
47
|
normalized.diagnostics = normalizeDiagnosticsConfig(diagnosticsValue, `${source}.diagnostics`);
|
|
47
48
|
}
|
|
49
|
+
if (instructionsValue !== undefined) {
|
|
50
|
+
normalized.instructions = normalizeInstructionsConfig(instructionsValue, `${source}.instructions`);
|
|
51
|
+
}
|
|
48
52
|
return normalized;
|
|
49
53
|
}
|
|
50
54
|
export function mergeAgentNativeConfigs(base, override) {
|
|
@@ -91,6 +95,12 @@ export function mergeAgentNativeConfigs(base, override) {
|
|
|
91
95
|
...override.diagnostics,
|
|
92
96
|
}
|
|
93
97
|
: undefined,
|
|
98
|
+
instructions: base.instructions || override.instructions
|
|
99
|
+
? {
|
|
100
|
+
...base.instructions,
|
|
101
|
+
...override.instructions,
|
|
102
|
+
}
|
|
103
|
+
: undefined,
|
|
94
104
|
};
|
|
95
105
|
}
|
|
96
106
|
export function resolveAgentNativeConfig(input, context) {
|
|
@@ -175,6 +185,32 @@ function normalizeDiagnosticsConfig(value, source) {
|
|
|
175
185
|
? {}
|
|
176
186
|
: { failOnBuild: value.failOnBuild };
|
|
177
187
|
}
|
|
188
|
+
function normalizeInstructionsConfig(value, source) {
|
|
189
|
+
if (!isRecord(value)) {
|
|
190
|
+
throw new Error(`${source} must be an object`);
|
|
191
|
+
}
|
|
192
|
+
const result = {};
|
|
193
|
+
for (const audience of ["runtime", "development"]) {
|
|
194
|
+
const pathValue = value[audience];
|
|
195
|
+
if (pathValue === undefined)
|
|
196
|
+
continue;
|
|
197
|
+
if (typeof pathValue !== "string") {
|
|
198
|
+
throw new Error(`${source}.${audience} must be a relative file path`);
|
|
199
|
+
}
|
|
200
|
+
result[audience] = normalizeRelativeFilePath(pathValue, `${source}.${audience}`);
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
function normalizeRelativeFilePath(value, source) {
|
|
205
|
+
const normalized = value.trim().replaceAll("\\", "/");
|
|
206
|
+
if (!normalized ||
|
|
207
|
+
normalized.startsWith("/") ||
|
|
208
|
+
/^[A-Za-z]:\//.test(normalized) ||
|
|
209
|
+
normalized.split("/").some((segment) => segment === "..")) {
|
|
210
|
+
throw new Error(`${source} must be a non-empty relative file path inside the app root`);
|
|
211
|
+
}
|
|
212
|
+
return normalized;
|
|
213
|
+
}
|
|
178
214
|
function normalizeRequiredEnvKeys(value, source) {
|
|
179
215
|
if (value === undefined)
|
|
180
216
|
return undefined;
|
package/dist/db/client.d.ts
CHANGED
|
@@ -227,7 +227,19 @@ export declare function isBackgroundFunctionPoolContext(): boolean;
|
|
|
227
227
|
* actual context.
|
|
228
228
|
*/
|
|
229
229
|
export declare function describeDbError(err: unknown): string;
|
|
230
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Thrown INSTEAD of attempting, while an endpoint is in cooldown.
|
|
232
|
+
*
|
|
233
|
+
* The refused attempt's error is described in the message and deliberately NOT
|
|
234
|
+
* attached as `cause`: isConnectionError() walks `cause.code`/`cause.message`,
|
|
235
|
+
* so a cause would reclassify this as retryable and retryOnConnectionError
|
|
236
|
+
* would drive the storm straight back.
|
|
237
|
+
*/
|
|
238
|
+
export declare class DbConnectCooldownError extends Error {
|
|
239
|
+
code: string;
|
|
240
|
+
constructor(remainingMs: number, refusedBy: string);
|
|
241
|
+
}
|
|
242
|
+
export declare function guardNeonPool(pool: unknown, url: string, label?: string): void;
|
|
231
243
|
interface ClosablePool {
|
|
232
244
|
end(): Promise<unknown>;
|
|
233
245
|
}
|
package/dist/db/client.js
CHANGED
|
@@ -613,6 +613,9 @@ export function isTransientDatabaseError(err) {
|
|
|
613
613
|
const error = err;
|
|
614
614
|
const code = String(error?.code ?? error?.cause?.code ?? "");
|
|
615
615
|
if (code === "ECHECKOUTTIMEOUT" ||
|
|
616
|
+
// Shed load during a connect cooldown is transient by construction — the
|
|
617
|
+
// endpoint refused, we chose not to re-ask yet. Surfaces as 503, not 500.
|
|
618
|
+
code === "DB_CONNECT_COOLDOWN" ||
|
|
616
619
|
code === "EMAXCONN" ||
|
|
617
620
|
code === "53300" ||
|
|
618
621
|
code === "57014" ||
|
|
@@ -960,7 +963,77 @@ export function describeDbError(err) {
|
|
|
960
963
|
}
|
|
961
964
|
return String(err);
|
|
962
965
|
}
|
|
963
|
-
|
|
966
|
+
/**
|
|
967
|
+
* How long one endpoint stops ATTEMPTING to connect after an attempt failed.
|
|
968
|
+
* Jittered so concurrent function instances do not re-probe in lockstep.
|
|
969
|
+
*/
|
|
970
|
+
function connectCooldownMs() {
|
|
971
|
+
const raw = Number(process.env.DB_CONNECT_COOLDOWN_MS);
|
|
972
|
+
const base = Number.isFinite(raw) && raw > 0 ? raw : 2_000;
|
|
973
|
+
return Math.round(base * (0.5 + Math.random()));
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Thrown INSTEAD of attempting, while an endpoint is in cooldown.
|
|
977
|
+
*
|
|
978
|
+
* The refused attempt's error is described in the message and deliberately NOT
|
|
979
|
+
* attached as `cause`: isConnectionError() walks `cause.code`/`cause.message`,
|
|
980
|
+
* so a cause would reclassify this as retryable and retryOnConnectionError
|
|
981
|
+
* would drive the storm straight back.
|
|
982
|
+
*/
|
|
983
|
+
export class DbConnectCooldownError extends Error {
|
|
984
|
+
code = "DB_CONNECT_COOLDOWN";
|
|
985
|
+
constructor(remainingMs, refusedBy) {
|
|
986
|
+
super(`Database is refusing connection attempts; not attempting again for ${remainingMs}ms. Last failure: ${refusedBy}`);
|
|
987
|
+
this.name = "DbConnectCooldownError";
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
const connectCooldowns = new Map();
|
|
991
|
+
/**
|
|
992
|
+
* Neon rejects a connection ATTEMPT, not a connection: "Failed to acquire
|
|
993
|
+
* permit to connect to the database. Too many database connection attempts are
|
|
994
|
+
* currently ongoing." A failed acquire leaves the pool with zero idle clients,
|
|
995
|
+
* so the next execute() calls connect() again — and retryOnConnectionError
|
|
996
|
+
* backs off only 100ms. The process answers a refusal by manufacturing the next
|
|
997
|
+
* attempt, which is what keeps the refusal true. Production sat in this loop
|
|
998
|
+
* until the compute was restarted by hand.
|
|
999
|
+
*
|
|
1000
|
+
* Hold a short cooldown per endpoint so a refused attempt cannot produce the
|
|
1001
|
+
* next one. Checking out an ALREADY-IDLE client is not an attempt and passes
|
|
1002
|
+
* through, so a cooldown degrades throughput instead of taking the process
|
|
1003
|
+
* offline.
|
|
1004
|
+
*/
|
|
1005
|
+
function gateNeonConnect(pool, url, label) {
|
|
1006
|
+
const connect = pool.connect;
|
|
1007
|
+
if (typeof connect !== "function")
|
|
1008
|
+
return;
|
|
1009
|
+
const gated = function gatedConnect(...args) {
|
|
1010
|
+
const idle = typeof pool.idleCount === "number" ? pool.idleCount : 0;
|
|
1011
|
+
const gate = connectCooldowns.get(url);
|
|
1012
|
+
if (idle === 0 && gate && Date.now() < gate.until) {
|
|
1013
|
+
return Promise.reject(new DbConnectCooldownError(gate.until - Date.now(), gate.refusedBy));
|
|
1014
|
+
}
|
|
1015
|
+
return Promise.resolve(connect.apply(this, args)).then((client) => {
|
|
1016
|
+
connectCooldowns.delete(url);
|
|
1017
|
+
return client;
|
|
1018
|
+
}, (err) => {
|
|
1019
|
+
const refusedBy = describeDbError(err);
|
|
1020
|
+
const ms = connectCooldownMs();
|
|
1021
|
+
// One line per cooldown window, not per shed request.
|
|
1022
|
+
if (!gate || Date.now() >= gate.until) {
|
|
1023
|
+
console.warn(`[${label}] connection attempt refused; pausing attempts ${ms}ms:`, refusedBy);
|
|
1024
|
+
}
|
|
1025
|
+
connectCooldowns.set(url, { until: Date.now() + ms, refusedBy });
|
|
1026
|
+
throw err;
|
|
1027
|
+
});
|
|
1028
|
+
};
|
|
1029
|
+
// Carry the wrapped function's own properties onto the wrapper. A pool's
|
|
1030
|
+
// `connect` may be instrumented — metrics, tracing, a test spy — and
|
|
1031
|
+
// replacing it with a bare closure would silently drop that instrumentation
|
|
1032
|
+
// along with any assertions built on it.
|
|
1033
|
+
Object.assign(gated, connect);
|
|
1034
|
+
pool.connect = gated;
|
|
1035
|
+
}
|
|
1036
|
+
export function guardNeonPool(pool, url, label = "db/neon") {
|
|
964
1037
|
if (!pool || typeof pool !== "object")
|
|
965
1038
|
return;
|
|
966
1039
|
if (loggedNeonPools.has(pool))
|
|
@@ -969,6 +1042,7 @@ export function attachNeonPoolErrorLogger(pool, label = "db/neon") {
|
|
|
969
1042
|
if (typeof withEvents.on !== "function")
|
|
970
1043
|
return;
|
|
971
1044
|
loggedNeonPools.add(pool);
|
|
1045
|
+
gateNeonConnect(pool, url, label);
|
|
972
1046
|
withEvents.on("error", (err) => {
|
|
973
1047
|
console.warn(`[${label}] pool error (will reconnect on next query):`, describeDbError(err));
|
|
974
1048
|
});
|
|
@@ -1177,7 +1251,7 @@ async function createDbExecInternal(config = {}, trackSingletonResources = false
|
|
|
1177
1251
|
const pool = trackSingletonResources
|
|
1178
1252
|
? sharedDbPool("neon", url, makePool)
|
|
1179
1253
|
: makePool();
|
|
1180
|
-
|
|
1254
|
+
guardNeonPool(pool, url);
|
|
1181
1255
|
const httpSql = bgHttp ? neon(url, { fullResults: true }) : null;
|
|
1182
1256
|
async function queryNeonClient(client, sql, timeoutOverrideMs) {
|
|
1183
1257
|
const { rawSql, args } = sqlAndArgs(sql);
|
package/dist/db/create-get-db.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { drizzle as drizzleD1 } from "drizzle-orm/d1";
|
|
3
|
-
import { getDialect, getCloudflareD1Binding, getDatabaseUrl, getDatabaseAuthToken, isLocalSqliteUrl, isPgliteUrl, isConnectionError, getPgliteClient, loadPgliteDrizzle, prepareLocalSqliteUrl, sqliteFilenameFromUrl, pgPoolOptions, neonPoolOptions,
|
|
3
|
+
import { getDialect, getCloudflareD1Binding, getDatabaseUrl, getDatabaseAuthToken, isLocalSqliteUrl, isPgliteUrl, isConnectionError, getPgliteClient, loadPgliteDrizzle, prepareLocalSqliteUrl, sqliteFilenameFromUrl, pgPoolOptions, neonPoolOptions, guardNeonPool, withDbTimeout, retryOnConnectionError, dbOpTimeoutMs, sharedDbPool, onSharedDbPoolsClosed, onSharedDbPoolReplaced, } from "./client.js";
|
|
4
4
|
// Lazy driver loaders — cached promises so dynamic import only runs once.
|
|
5
5
|
let _pgDrizzle;
|
|
6
6
|
function getPgDrizzle() {
|
|
@@ -408,7 +408,7 @@ export function createGetDb(schema) {
|
|
|
408
408
|
// schema module. See `sharedDbPool` in client.ts.
|
|
409
409
|
resetOnPoolClose("neon", url);
|
|
410
410
|
const rawPool = sharedDbPool("neon", url, () => new Pool({ connectionString: url, ...neonPoolOptions() }));
|
|
411
|
-
|
|
411
|
+
guardNeonPool(rawPool, url);
|
|
412
412
|
// Wrap the pool with the resilience layer so Drizzle queries get the
|
|
413
413
|
// same withDbTimeout + retryOnConnectionError protection as the raw
|
|
414
414
|
// DbExec path in client.ts. Reads retry freely; writes only retry on
|
package/dist/db/migrations.js
CHANGED
|
@@ -245,11 +245,28 @@ function isServerlessRequestRuntime() {
|
|
|
245
245
|
process.env.VERCEL === "1");
|
|
246
246
|
}
|
|
247
247
|
/**
|
|
248
|
-
*
|
|
249
|
-
*
|
|
248
|
+
* Whether this app migrates at RELEASE time, so skipping on the request path
|
|
249
|
+
* is safe.
|
|
250
250
|
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
251
|
+
* Without this, "skip in serverless" silently means "never migrate" for any app
|
|
252
|
+
* that has no release entrypoint — and at the time this was written exactly one
|
|
253
|
+
* of seventeen templates had one. Existing tables keep working, so nothing
|
|
254
|
+
* fails; a newly added migration just never applies, and a fresh deploy comes
|
|
255
|
+
* up with missing tables. That is a skip indistinguishable from success, which
|
|
256
|
+
* is the failure mode this repo bans.
|
|
257
|
+
*
|
|
258
|
+
* So the request-path skip is opt-in per app. An app sets this once it owns a
|
|
259
|
+
* release migration step (see templates/analytics/scripts/migrate-production.ts
|
|
260
|
+
* and the `migrate:production` build step in its netlify.toml).
|
|
261
|
+
*/
|
|
262
|
+
function appMigratesAtRelease() {
|
|
263
|
+
const raw = process.env.AGENT_NATIVE_RELEASE_MIGRATIONS?.trim();
|
|
264
|
+
return !!raw && ["1", "true", "yes", "on"].includes(raw.toLowerCase());
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* A runtime that is ALLOWED to migrate: release scripts, scheduled jobs, and
|
|
268
|
+
* durable background workers, which are off the request path and may take as
|
|
269
|
+
* long as they need. Claimed with {@link withMigrationRuntime}.
|
|
253
270
|
*/
|
|
254
271
|
function isMigrationAuthorizedRuntime() {
|
|
255
272
|
return (globalThis.__AGENT_NATIVE_MIGRATION_RUNTIME__ === true);
|
|
@@ -322,6 +339,7 @@ export function runMigrations(migrations, options) {
|
|
|
322
339
|
// the explicit opt-in for a caller that genuinely cannot defer.
|
|
323
340
|
if (options?.runInServerlessRequest !== true &&
|
|
324
341
|
isServerlessRequestRuntime() &&
|
|
342
|
+
appMigratesAtRelease() &&
|
|
325
343
|
!isMigrationAuthorizedRuntime()) {
|
|
326
344
|
console.info(`[migrations] Skipping "${table}" migrations in a serverless request runtime`);
|
|
327
345
|
return;
|
package/dist/deploy/build.d.ts
CHANGED
|
@@ -312,3 +312,4 @@ export declare function resolveNitroBundledYjsEntry(): string;
|
|
|
312
312
|
* the small set above bundled to keep their package manifests traceable.
|
|
313
313
|
*/
|
|
314
314
|
export declare function nitroNoExternalsForPreset(targetPreset: string): true | readonly string[];
|
|
315
|
+
export declare function resolveNitroBuildReplacements(env?: NodeJS.ProcessEnv): Record<string, string>;
|
package/dist/deploy/build.js
CHANGED
|
@@ -23,10 +23,11 @@ import { INTEGRATION_RECOVERY_RUNTIME_MARKER, INTEGRATION_RETRY_SWEEP_PATH, INTE
|
|
|
23
23
|
import { isValidCron } from "../jobs/cron.js";
|
|
24
24
|
import { RECURRING_JOBS_SWEEP_PATH, RECURRING_JOBS_SWEEP_TOKEN_SUBJECT, } from "../jobs/scheduler-dispatch.js";
|
|
25
25
|
import { normalizeAppBasePath } from "../server/app-base-path.js";
|
|
26
|
-
import { DEFAULT_SPECULATION_RULES_PATH, resolveSsrCacheHeaders, } from "../shared/cache-control.js";
|
|
26
|
+
import { DEFAULT_SPECULATION_RULES_PATH, resolveSsrCacheHeaders, resolveSsrCacheKeyHeaders, } from "../shared/cache-control.js";
|
|
27
27
|
import { mcpEmbedStaticAssetRouteRules } from "../shared/mcp-embed-headers.js";
|
|
28
28
|
import { AGENT_NATIVE_SOCIAL_IMAGE_ALT, AGENT_NATIVE_SOCIAL_IMAGE_CACHE_BUSTER, AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT, AGENT_NATIVE_SOCIAL_IMAGE_PATH, AGENT_NATIVE_SOCIAL_IMAGE_TYPE, AGENT_NATIVE_SOCIAL_IMAGE_WIDTH, } from "../shared/social-meta.js";
|
|
29
29
|
import { generateActionRegistryForProject } from "../vite/action-types-plugin.js";
|
|
30
|
+
import { createAgentNativeConfigContext, loadResolvedAgentNativeConfig, } from "../vite/agent-native-config-loader.js";
|
|
30
31
|
import { cloneServerBundleForFunction, copyDir } from "./function-bundle.js";
|
|
31
32
|
import { collectImmutableAssetPaths, IMMUTABLE_ASSET_CACHE_CONTROL, IMMUTABLE_ASSET_CACHE_HEADERS, prefixAssetPath, } from "./immutable-assets.js";
|
|
32
33
|
import { discoverApiRoutes, discoverPlugins, discoverActionFiles, getMissingDefaultPlugins, DEFAULT_PLUGIN_REGISTRY, } from "./route-discovery.js";
|
|
@@ -651,6 +652,7 @@ export function generateWorkerEntry(routes, pluginPaths, defaultPluginStems = []
|
|
|
651
652
|
// The worker ships as a static bundle with no access to runtime env, so the
|
|
652
653
|
// deployment-wide SSR cache policy is baked in from this build's env.
|
|
653
654
|
const ssrCacheHeaders = resolveSsrCacheHeaders();
|
|
655
|
+
const ssrCacheKeyHeaders = resolveSsrCacheKeyHeaders();
|
|
654
656
|
const routeImports = [];
|
|
655
657
|
const routeRegistrations = [];
|
|
656
658
|
for (let i = 0; i < routes.length; i++) {
|
|
@@ -970,9 +972,8 @@ function injectHeadScript(html, script) {
|
|
|
970
972
|
}
|
|
971
973
|
|
|
972
974
|
// Resolved from AGENT_NATIVE_SSR_CACHE at build time.
|
|
973
|
-
const
|
|
974
|
-
const
|
|
975
|
-
const SSR_NETLIFY_CDN_CACHE_CONTROL = ${JSON.stringify(ssrCacheHeaders["netlify-cdn-cache-control"])};
|
|
975
|
+
const SSR_CACHE_HEADERS = ${JSON.stringify(ssrCacheHeaders)};
|
|
976
|
+
const SSR_CACHE_KEY_HEADERS = ${JSON.stringify(ssrCacheKeyHeaders)};
|
|
976
977
|
const DEFAULT_SPECULATION_RULES_PATH = ${JSON.stringify(DEFAULT_SPECULATION_RULES_PATH)};
|
|
977
978
|
const IMMUTABLE_ASSET_CACHE_CONTROL = ${JSON.stringify(IMMUTABLE_ASSET_CACHE_CONTROL)};
|
|
978
979
|
const IMMUTABLE_ASSET_PATHS = new Set(${JSON.stringify([...new Set(immutableAssetPaths)].sort())});
|
|
@@ -1056,13 +1057,12 @@ function applyDefaultSsrCacheHeader(headers, status, pathname) {
|
|
|
1056
1057
|
else headers.delete("vary");
|
|
1057
1058
|
}
|
|
1058
1059
|
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
headers.set("netlify-cdn-cache-control", SSR_NETLIFY_CDN_CACHE_CONTROL);
|
|
1060
|
+
for (const [name, value] of Object.entries(SSR_CACHE_HEADERS)) {
|
|
1061
|
+
headers.set(name, value);
|
|
1062
|
+
}
|
|
1063
|
+
for (const [name, value] of Object.entries(SSR_CACHE_KEY_HEADERS)) {
|
|
1064
|
+
headers.set(name, value);
|
|
1065
|
+
}
|
|
1066
1066
|
}
|
|
1067
1067
|
|
|
1068
1068
|
function applyDefaultSpeculationRulesHeader(headers, status, basePath) {
|
|
@@ -3463,6 +3463,22 @@ function createBrowserOnlyServerStubPlugin() {
|
|
|
3463
3463
|
},
|
|
3464
3464
|
};
|
|
3465
3465
|
}
|
|
3466
|
+
export function resolveNitroBuildReplacements(env = process.env) {
|
|
3467
|
+
return {
|
|
3468
|
+
// Netlify exposes DEPLOY_ID only while building. Embed it into the Nitro
|
|
3469
|
+
// function so preview OAuth relays can target this immutable deployment
|
|
3470
|
+
// even though the value is unavailable in the function runtime.
|
|
3471
|
+
"process.env.AGENT_NATIVE_BUILD_ID": JSON.stringify(env.DEPLOY_ID?.trim() || env.AGENT_NATIVE_BUILD_ID?.trim() || ""),
|
|
3472
|
+
"process.env.AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID": JSON.stringify(env.GA_MEASUREMENT_ID?.trim() || ""),
|
|
3473
|
+
"process.env.AGENT_NATIVE_BUILD_GTM_CONTAINER_ID": JSON.stringify(env.GTM_CONTAINER_ID?.trim() || ""),
|
|
3474
|
+
// Netlify's netlify.toml environment is available while this deploy
|
|
3475
|
+
// build runs, but is not injected into the deployed Function. Nitro is
|
|
3476
|
+
// a separate server build, so embed release migration ownership here as
|
|
3477
|
+
// well as in the Vite server bundle.
|
|
3478
|
+
"process.env.AGENT_NATIVE_RELEASE_MIGRATIONS": JSON.stringify(env.AGENT_NATIVE_RELEASE_MIGRATIONS?.trim() || ""),
|
|
3479
|
+
"process.env.AGENT_NATIVE_BUILD_DEPLOY_CONTEXT": JSON.stringify(env.CONTEXT?.trim() || ""),
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3466
3482
|
async function buildWithNitro() {
|
|
3467
3483
|
console.log(`[deploy] Building for preset "${preset}" via Nitro...`);
|
|
3468
3484
|
const appBasePath = normalizeConfiguredAppBasePath();
|
|
@@ -3493,6 +3509,7 @@ async function buildWithNitro() {
|
|
|
3493
3509
|
// own virtual module registration. Both paths reuse `readAgentsBundleFromFs`
|
|
3494
3510
|
// from `server/agents-bundle.ts` to guarantee identical content.
|
|
3495
3511
|
const { readAgentsBundleFromFs } = await import("../server/agents-bundle.js");
|
|
3512
|
+
const nitroAgentConfig = await loadResolvedAgentNativeConfig(cwd, createAgentNativeConfigContext("build", process.env.NODE_ENV === "development" ? "development" : "production"));
|
|
3496
3513
|
// Resolve the workspace core (if present) up front so the bundle embeds
|
|
3497
3514
|
// enterprise-wide AGENTS.md + skills alongside the template's.
|
|
3498
3515
|
const nitroWorkspaceCore = await getWorkspaceCoreExports(cwd);
|
|
@@ -3504,7 +3521,9 @@ async function buildWithNitro() {
|
|
|
3504
3521
|
}
|
|
3505
3522
|
: null;
|
|
3506
3523
|
const agentsBundleModuleSource = () => {
|
|
3507
|
-
const bundle = readAgentsBundleFromFs(cwd, nitroWorkspaceSource
|
|
3524
|
+
const bundle = readAgentsBundleFromFs(cwd, nitroWorkspaceSource, {
|
|
3525
|
+
instructions: nitroAgentConfig.instructions,
|
|
3526
|
+
});
|
|
3508
3527
|
return `// AUTO-GENERATED by @agent-native/core deploy build (Nitro virtual)
|
|
3509
3528
|
// Contains the inlined AGENTS.md + .agents/skills/ content from the template,
|
|
3510
3529
|
// merged with the workspace core's AGENTS.md + skills/ when present.
|
|
@@ -3543,17 +3562,7 @@ export default bundle;
|
|
|
3543
3562
|
virtual: {
|
|
3544
3563
|
"virtual:agents-bundle": agentsBundleModuleSource,
|
|
3545
3564
|
},
|
|
3546
|
-
replace:
|
|
3547
|
-
// Netlify exposes DEPLOY_ID only while building. Embed it into the Nitro
|
|
3548
|
-
// function so preview OAuth relays can target this immutable deployment
|
|
3549
|
-
// even though the value is unavailable in the function runtime.
|
|
3550
|
-
"process.env.AGENT_NATIVE_BUILD_ID": JSON.stringify(process.env.DEPLOY_ID?.trim() ||
|
|
3551
|
-
process.env.AGENT_NATIVE_BUILD_ID?.trim() ||
|
|
3552
|
-
""),
|
|
3553
|
-
"process.env.AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID": JSON.stringify(process.env.GA_MEASUREMENT_ID?.trim() || ""),
|
|
3554
|
-
"process.env.AGENT_NATIVE_BUILD_GTM_CONTAINER_ID": JSON.stringify(process.env.GTM_CONTAINER_ID?.trim() || ""),
|
|
3555
|
-
"process.env.AGENT_NATIVE_BUILD_DEPLOY_CONTEXT": JSON.stringify(process.env.CONTEXT?.trim() || ""),
|
|
3556
|
-
},
|
|
3565
|
+
replace: resolveNitroBuildReplacements(),
|
|
3557
3566
|
// Replace browser-only renderers (Excalidraw/Mermaid) with an inert proxy in
|
|
3558
3567
|
// the server bundle. Without this, Nitro's Rolldown build pulls the real
|
|
3559
3568
|
// Excalidraw into a shared vendor chunk imported statically by the SSR render
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
export declare function createNotificationsHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<"" | import("./types.js").Notification[] | {
|
|
14
14
|
count: number;
|
|
15
15
|
updated?: undefined;
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error?: undefined;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
count?: undefined;
|
|
20
20
|
updated: number;
|
|
21
|
-
ok?: undefined;
|
|
22
21
|
error?: undefined;
|
|
22
|
+
ok?: undefined;
|
|
23
23
|
} | {
|
|
24
24
|
count?: undefined;
|
|
25
25
|
updated?: undefined;
|
|
@@ -28,6 +28,6 @@ export declare function createNotificationsHandler(): import("h3").EventHandlerW
|
|
|
28
28
|
} | {
|
|
29
29
|
count?: undefined;
|
|
30
30
|
updated?: undefined;
|
|
31
|
-
ok: boolean;
|
|
32
31
|
error?: undefined;
|
|
32
|
+
ok: boolean;
|
|
33
33
|
}>>;
|