@agent-native/core 0.161.22 → 0.162.0
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/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +18 -2
- package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/calendar/server/handlers/google-auth.ts +1 -8
- package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +88 -35
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +36 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -3
- package/corpus/templates/clips/desktop/src/app.tsx +30 -40
- package/corpus/templates/clips/desktop/src/components/MicOffConfirmation.tsx +47 -0
- package/corpus/templates/clips/desktop/src/styles.css +110 -0
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +114 -7
- package/corpus/templates/clips/shared/recording-capture.ts +18 -13
- package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +3 -5
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +0 -3
- package/corpus/templates/content/app/components/editor/NotionConflictBanner.tsx +0 -4
- package/corpus/templates/content/app/components/editor/NotionSyncBar.tsx +4 -1
- package/corpus/templates/content/app/hooks/use-notion.ts +17 -15
- package/corpus/templates/crm/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/design/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +13 -7
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +18 -21
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +2 -5
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +4 -12
- package/corpus/templates/design/app/pages/design-editor/types.ts +38 -1
- package/corpus/templates/design/changelog/2026-08-18-advanced-design-editor-panels-hidden-by-default.md +6 -0
- package/corpus/templates/design/playwright.config.ts +19 -2
- package/corpus/templates/dispatch/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/factory/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/macros/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +18 -3
- package/corpus/templates/mail/server/handlers/google-auth.ts +1 -8
- package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +4 -0
- package/corpus/templates/slides/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/slides/actions/_await-fit-check.ts +30 -5
- package/corpus/templates/slides/actions/add-slide.ts +6 -2
- package/corpus/templates/slides/actions/create-deck.ts +2 -1
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +1 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +34 -1
- package/corpus/templates/slides/app/routes/p.$id.tsx +3 -1
- package/corpus/templates/slides/changelog/2026-08-18-generated-slides-preserve-speaker-notes.md +6 -0
- package/corpus/templates/slides/server/handlers/assets.ts +23 -12
- package/corpus/templates/slides/server/handlers/design-system-decode-job-status.ts +15 -6
- package/corpus/templates/slides/server/handlers/design-system-upload-start.ts +15 -6
- package/corpus/templates/slides/server/handlers/index-design-system-sources.ts +34 -19
- package/corpus/templates/slides/server/handlers/share.ts +1 -1
- package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/slides/server/routes/api/deck-agent-context.json.get.ts +1 -1
- package/corpus/templates/slides/server/routes/api/exports/[filename].get.ts +9 -3
- package/corpus/templates/slides/server/routes/api/exports/google-slides.post.ts +12 -5
- package/corpus/templates/slides/server/routes/api/exports/html.post.ts +9 -7
- package/corpus/templates/slides/server/routes/api/exports/pptx.post.ts +9 -7
- package/corpus/templates/slides/shared/api.ts +7 -1
- package/corpus/templates/tasks/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/agent/engine/builder-engine.js +15 -8
- package/dist/agent/engine/translate-anthropic.js +27 -2
- package/dist/agent/production-agent.d.ts +17 -2
- package/dist/agent/production-agent.js +48 -17
- package/dist/agent/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.js +6 -4
- package/dist/agent/tool-approval-store.d.ts +10 -1
- package/dist/agent/tool-approval-store.js +19 -2
- package/dist/agent/types.d.ts +9 -0
- package/dist/client/AssistantChat.js +14 -6
- package/dist/client/EnvironmentBadge.d.ts +15 -0
- package/dist/client/EnvironmentBadge.js +79 -0
- package/dist/client/MultiTabAssistantChat.js +73 -17
- package/dist/client/agent-chat-adapter.js +11 -1
- package/dist/client/api-path.js +14 -1
- package/dist/client/app-providers.js +2 -1
- package/dist/client/chat/tool-call-display.d.ts +12 -2
- package/dist/client/chat/tool-call-display.js +17 -7
- package/dist/client/chat/widgets/WorkspaceFileWidget.js +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts +6 -0
- package/dist/client/chat/widgets/builtin-tool-renderers.js +19 -0
- package/dist/client/onboarding/FirstRunOnboarding.js +46 -32
- package/dist/client/onboarding/use-onboarding.d.ts +8 -1
- package/dist/client/onboarding/use-onboarding.js +22 -7
- package/dist/client/session-replay.js +58 -15
- package/dist/client/settings/SettingsTabsPage.js +19 -6
- package/dist/client/sse-event-processor.d.ts +7 -0
- package/dist/client/sse-event-processor.js +4 -1
- package/dist/client/ui/index.d.ts +1 -0
- package/dist/client/ui/index.js +1 -0
- package/dist/client/use-action.js +26 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/config.d.ts +15 -0
- package/dist/config.js +53 -0
- package/dist/extensions/fetch-tool.js +8 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/embed-app.js +1 -1
- 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 +13 -13
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/index.d.ts +10 -0
- package/dist/provider-api/index.js +45 -9
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +10 -3
- package/dist/server/poll.js +7 -3
- package/dist/server/sentry.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/vite/client.d.ts +5 -0
- package/dist/vite/client.js +64 -2
- package/dist/workspace-files/actions.d.ts +1 -8
- package/dist/workspace-files/actions.js +5 -21
- package/dist/workspace-files/index.d.ts +1 -1
- package/dist/workspace-files/index.js +1 -1
- package/dist/workspace-files/store.d.ts +18 -0
- package/dist/workspace-files/store.js +24 -3
- package/package.json +3 -3
- package/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Skeleton } from "@agent-native/toolkit/ui/skeleton";
|
|
3
3
|
import { IconArrowRight, IconBrandGithub, IconCheck, IconExternalLink, IconInfoCircle, IconKey, IconLoader2, IconSearch, } from "@tabler/icons-react";
|
|
4
|
-
import React, { useMemo, useState } from "react";
|
|
4
|
+
import React, { useCallback, useMemo, useState } from "react";
|
|
5
5
|
import { docsUrl } from "../../shared/docs-url.js";
|
|
6
6
|
import { appPath } from "../api-path.js";
|
|
7
7
|
import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
|
|
@@ -28,7 +28,14 @@ const BUILDER_MORE_SERVICES = [
|
|
|
28
28
|
];
|
|
29
29
|
export function FirstRunOnboarding({ skipIntegrations = shouldSkipFirstRunIntegrations(), initialFirstRun = false, } = {}) {
|
|
30
30
|
const previewMode = useOnboardingPreviewMode();
|
|
31
|
-
const { firstRun, loading, error, profile, completeFirstRun } = useOnboarding({ preview: previewMode, initialFirstRun });
|
|
31
|
+
const { firstRun, loading, error, profile, completeFirstRun, completeFirstRunError, } = useOnboarding({ preview: previewMode, initialFirstRun });
|
|
32
|
+
// completeFirstRun() rejects on failure — swallow it here so a Skip/
|
|
33
|
+
// Continue click never becomes an unhandled rejection; completeFirstRunError
|
|
34
|
+
// (rendered below) is the real signal, and the user stays on this screen
|
|
35
|
+
// to retry instead of being bounced to an unrelated error screen.
|
|
36
|
+
const finishOnboarding = useCallback(() => {
|
|
37
|
+
completeFirstRun().catch(() => { });
|
|
38
|
+
}, [completeFirstRun]);
|
|
32
39
|
const [screen, setScreen] = useState("intro");
|
|
33
40
|
const [extensionIndex, setExtensionIndex] = useState(0);
|
|
34
41
|
const [integrationQuery, setIntegrationQuery] = useState("");
|
|
@@ -86,15 +93,15 @@ export function FirstRunOnboarding({ skipIntegrations = shouldSkipFirstRunIntegr
|
|
|
86
93
|
trackingFlow: "connect_llm",
|
|
87
94
|
});
|
|
88
95
|
};
|
|
89
|
-
const handleOpenSettings =
|
|
96
|
+
const handleOpenSettings = () => {
|
|
90
97
|
window.dispatchEvent(new CustomEvent("agent-panel:open-settings", {
|
|
91
98
|
detail: { section: "integrations" },
|
|
92
99
|
}));
|
|
93
|
-
|
|
100
|
+
finishOnboarding();
|
|
94
101
|
};
|
|
95
102
|
const handleFinish = () => {
|
|
96
103
|
if (extensions.length === 0) {
|
|
97
|
-
|
|
104
|
+
finishOnboarding();
|
|
98
105
|
return;
|
|
99
106
|
}
|
|
100
107
|
setExtensionIndex(0);
|
|
@@ -155,7 +162,7 @@ export function FirstRunOnboarding({ skipIntegrations = shouldSkipFirstRunIntegr
|
|
|
155
162
|
if (screen === "extension") {
|
|
156
163
|
const extension = extensions[extensionIndex];
|
|
157
164
|
if (!extension) {
|
|
158
|
-
|
|
165
|
+
finishOnboarding();
|
|
159
166
|
return null;
|
|
160
167
|
}
|
|
161
168
|
const Extension = extension.component;
|
|
@@ -164,9 +171,9 @@ export function FirstRunOnboarding({ skipIntegrations = shouldSkipFirstRunIntegr
|
|
|
164
171
|
setExtensionIndex((current) => current + 1);
|
|
165
172
|
return;
|
|
166
173
|
}
|
|
167
|
-
|
|
174
|
+
finishOnboarding();
|
|
168
175
|
};
|
|
169
|
-
return (_jsx(Extension, { onComplete: advanceExtension, onSkip:
|
|
176
|
+
return (_jsxs(_Fragment, { children: [_jsx(Extension, { onComplete: advanceExtension, onSkip: finishOnboarding }), completeFirstRunError && (_jsx(FirstRunCompletionError, { message: completeFirstRunError, onRetry: finishOnboarding }))] }));
|
|
170
177
|
}
|
|
171
178
|
if (screen === "intro") {
|
|
172
179
|
return (_jsx(OnboardingShell, { profile: profile, screen: "intro", children: _jsxs("div", { className: "mx-auto flex w-full max-w-lg flex-col items-center text-center", children: [_jsxs("h1", { className: "text-3xl font-semibold tracking-[-0.05em] sm:text-4xl", children: ["Free forever.", _jsx("br", {}), _jsx("span", { className: "text-primary", children: "Open source for life." })] }), _jsxs("div", { className: "mt-7 grid w-full gap-2 text-left sm:grid-cols-3", children: [_jsxs("div", { className: "rounded-lg bg-muted/35 px-3 py-3", children: [_jsx("p", { className: "text-xs font-medium", children: "Fully customizable" }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: "Change the UI, code, and behavior." })] }), _jsxs("div", { className: "rounded-lg bg-muted/35 px-3 py-3", children: [_jsx("p", { className: "text-xs font-medium", children: "Bring your own keys" }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: "Use your own providers and accounts." })] }), _jsxs("div", { className: "rounded-lg bg-muted/35 px-3 py-3", children: [_jsx("p", { className: "text-xs font-medium", children: "Build your own" }), _jsx("p", { className: "mt-1 text-[11px] leading-4 text-muted-foreground", children: "Mix and match toolkit pieces in your own apps." })] })] }), _jsxs("button", { type: "button", className: cn(primaryButtonClass, "mt-6"), onClick: () => setScreen("choice"), children: ["Continue", _jsx(IconArrowRight, { size: 15 })] }), _jsxs("a", { href: "https://github.com/builderio/agent-native", target: "_blank", rel: "noreferrer", className: "mt-3 inline-flex items-center gap-1.5 text-xs text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [_jsx(IconBrandGithub, { size: 14 }), _jsx("span", { children: "View source" }), _jsx(IconExternalLink, { size: 13 })] })] }) }));
|
|
@@ -204,29 +211,29 @@ export function FirstRunOnboarding({ skipIntegrations = shouldSkipFirstRunIntegr
|
|
|
204
211
|
if (screen === "connecting") {
|
|
205
212
|
return (_jsx(OnboardingShell, { profile: profile, screen: "choice", children: _jsxs("div", { className: "mx-auto flex w-full max-w-md flex-col items-center text-center", children: [_jsx("div", { className: "flex size-10 items-center justify-center rounded-full bg-primary/10 text-primary", children: _jsx(IconLoader2, { className: "animate-spin", size: 19 }) }), _jsx("h1", { className: "mt-5 text-xl font-semibold tracking-[-0.04em]", children: "Connecting Builder.io free credits" }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Finish the one-click connection in the new window." }), _jsxs("div", { className: "mt-7 w-full rounded-xl bg-muted/35 p-4 text-left", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Skeleton, { className: "h-3 w-28" }), _jsx(Skeleton, { className: "h-5 w-16 rounded-full" })] }), _jsx(Skeleton, { className: "mt-4 h-8 w-full" }), _jsxs("div", { className: "mt-3 grid grid-cols-3 gap-2", children: [_jsx(Skeleton, { className: "h-7 w-full" }), _jsx(Skeleton, { className: "h-7 w-full" }), _jsx(Skeleton, { className: "h-7 w-full" })] })] }), connectFlow.error && (_jsxs("div", { className: "mt-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-xs text-destructive", children: connectFlow.error }), _jsx("button", { type: "button", className: secondaryButtonClass, onClick: () => setScreen("choice"), children: "Try again" })] }))] }) }));
|
|
206
213
|
}
|
|
207
|
-
return (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
214
|
+
return (_jsxs(OnboardingShell, { profile: profile, screen: "ready", children: [_jsxs("div", { className: "mx-auto flex w-full max-w-2xl flex-col items-center text-center", children: [_jsx("div", { className: "flex size-12 items-center justify-center rounded-2xl bg-primary/10 text-primary", children: _jsx(IconCheck, { size: 20 }) }), _jsx("h1", { className: "mt-5 text-xl font-semibold tracking-[-0.04em]", children: "Your agent is ready." }), _jsx("p", { className: "mt-2 max-w-md text-sm leading-6 text-muted-foreground", children: "Start with a chat, then connect more tools whenever you need them." }), _jsx("div", { className: "mt-7 grid w-full gap-2 text-left sm:grid-cols-3", children: (skipIntegrations
|
|
215
|
+
? [
|
|
216
|
+
[
|
|
217
|
+
"Workflow actions",
|
|
218
|
+
"Use the app's buttons and sidebar to run the workflow.",
|
|
219
|
+
],
|
|
220
|
+
[
|
|
221
|
+
"AI sidebar",
|
|
222
|
+
"Ask the agent to review or refine a step in context.",
|
|
223
|
+
],
|
|
224
|
+
[
|
|
225
|
+
"Flexible providers",
|
|
226
|
+
"Use Builder.io free credits or your own keys.",
|
|
227
|
+
],
|
|
228
|
+
]
|
|
229
|
+
: [
|
|
230
|
+
["Chat + actions", "Ask your agent to work across the app."],
|
|
231
|
+
["Agent integrations", "Connect tools from Settings anytime."],
|
|
232
|
+
[
|
|
233
|
+
"Flexible providers",
|
|
234
|
+
"Use Builder.io free credits or your own keys.",
|
|
235
|
+
],
|
|
236
|
+
]).map(([title, description]) => (_jsxs("div", { className: "rounded-xl bg-muted/35 px-4 py-4", children: [_jsx("span", { className: "flex size-7 items-center justify-center rounded-full bg-primary/10 text-primary", children: _jsx(IconCheck, { size: 14 }) }), _jsx("p", { className: "mt-3 text-sm font-medium", children: title }), _jsx("p", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: description })] }, title))) }), _jsxs("button", { type: "button", "data-testid": "first-run-open-app", className: cn(primaryButtonClass, "mt-7"), onClick: handleFinish, children: ["Open app", _jsx(IconArrowRight, { size: 15 })] })] }), completeFirstRunError && (_jsx(FirstRunCompletionError, { message: completeFirstRunError, onRetry: finishOnboarding }))] }));
|
|
230
237
|
}
|
|
231
238
|
function OnboardingShell({ profile, screen, footer, children, }) {
|
|
232
239
|
return (_jsxs("div", { className: "fixed inset-0 z-[100] flex h-full min-h-0 flex-col bg-background text-foreground", "data-onboarding-screen": screen, role: "dialog", "aria-modal": "true", "aria-label": `${profile?.appName ?? "Your app"} setup`, children: [_jsx("div", { className: "h-0.5 shrink-0 bg-muted", "data-testid": "onboarding-progress", children: _jsx("div", { className: "h-full bg-primary transition-[width] duration-200", style: {
|
|
@@ -266,6 +273,13 @@ function CapabilityInfoButton({ capability, ariaLabel, }) {
|
|
|
266
273
|
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": ariaLabel, className: "inline-flex size-4 items-center justify-center rounded-full text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: _jsx(IconInfoCircle, { size: 13 }) }) }), _jsx(TooltipContent, { side: "top", className: "max-w-xs text-xs", children: capability.why })] }));
|
|
267
274
|
}
|
|
268
275
|
const primaryButtonClass = "inline-flex min-h-9 items-center justify-center gap-2 rounded-lg bg-primary px-4 text-xs font-medium text-primary-foreground shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
276
|
+
/** Inline failure signal for a failed completeFirstRun() call — keeps the
|
|
277
|
+
* user on their current screen with a way forward, instead of swapping to
|
|
278
|
+
* an unrelated full-screen error or leaving Skip/Continue looking like it
|
|
279
|
+
* did nothing. */
|
|
280
|
+
function FirstRunCompletionError({ message, onRetry, }) {
|
|
281
|
+
return (_jsxs("div", { className: "fixed inset-x-0 bottom-4 z-50 mx-auto flex w-fit max-w-[90vw] items-center gap-3 rounded-lg border border-destructive/30 bg-background px-4 py-2 text-xs shadow-lg", children: [_jsx("span", { className: "text-destructive", children: message }), _jsx("button", { type: "button", className: "font-medium underline underline-offset-2 hover:no-underline", onClick: onRetry, children: "Try again" })] }));
|
|
282
|
+
}
|
|
269
283
|
const secondaryButtonClass = "inline-flex min-h-9 items-center justify-center gap-2 rounded-lg border border-border bg-background px-4 text-xs font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";
|
|
270
284
|
function compareUrl(value) {
|
|
271
285
|
try {
|
|
@@ -30,8 +30,15 @@ export interface UseOnboardingResult {
|
|
|
30
30
|
reopen: () => Promise<void>;
|
|
31
31
|
/** True until the post-signup full-screen flow is completed. */
|
|
32
32
|
firstRun: boolean;
|
|
33
|
-
/** Clear the post-signup full-screen flow marker.
|
|
33
|
+
/** Clear the post-signup full-screen flow marker. Rejects instead of
|
|
34
|
+
* resolving silently when the server call fails — see
|
|
35
|
+
* `completeFirstRunError` for the message to show the user. */
|
|
34
36
|
completeFirstRun: () => Promise<void>;
|
|
37
|
+
/** Set when the last `completeFirstRun()` call failed. Cleared on the next
|
|
38
|
+
* attempt (success or failure). Distinct from `error` (the initial steps
|
|
39
|
+
* load failure) so a failed Skip/Continue doesn't swap the whole screen
|
|
40
|
+
* for an unrelated "could not load" message. */
|
|
41
|
+
completeFirstRunError: string | null;
|
|
35
42
|
}
|
|
36
43
|
export declare function useOnboarding(options?: {
|
|
37
44
|
preview?: boolean;
|
|
@@ -10,6 +10,7 @@ export function useOnboarding(options = {}) {
|
|
|
10
10
|
const [error, setError] = useState(null);
|
|
11
11
|
const [dismissed, setDismissed] = useState(false);
|
|
12
12
|
const [firstRun, setFirstRun] = useState(preview || initialFirstRun);
|
|
13
|
+
const [completeFirstRunError, setCompleteFirstRunError] = useState(null);
|
|
13
14
|
const mountedRef = useRef(true);
|
|
14
15
|
useEffect(() => {
|
|
15
16
|
setFirstRun(preview || initialFirstRun);
|
|
@@ -116,14 +117,27 @@ export function useOnboarding(options = {}) {
|
|
|
116
117
|
}
|
|
117
118
|
return;
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
setCompleteFirstRunError(null);
|
|
121
|
+
// Both a rejected fetch (offline, dropped connection) and a non-ok
|
|
122
|
+
// response are real failures — neither may look like success to the
|
|
123
|
+
// caller, so both throw instead of returning as if the step advanced.
|
|
124
|
+
let response;
|
|
125
|
+
try {
|
|
126
|
+
response = await fetch(agentNativePath("/_agent-native/onboarding/first-run/complete"), {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: { "Content-Type": "application/json" },
|
|
129
|
+
body: "{}",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
const message = e instanceof Error ? e.message : "first-run completion request failed";
|
|
134
|
+
setCompleteFirstRunError(message);
|
|
135
|
+
throw e instanceof Error ? e : new Error(message);
|
|
136
|
+
}
|
|
124
137
|
if (!response.ok) {
|
|
125
|
-
|
|
126
|
-
|
|
138
|
+
const message = `first-run completion failed: ${response.status}`;
|
|
139
|
+
setCompleteFirstRunError(message);
|
|
140
|
+
throw new Error(message);
|
|
127
141
|
}
|
|
128
142
|
setFirstRun(false);
|
|
129
143
|
if (typeof window !== "undefined") {
|
|
@@ -153,5 +167,6 @@ export function useOnboarding(options = {}) {
|
|
|
153
167
|
reopen,
|
|
154
168
|
firstRun,
|
|
155
169
|
completeFirstRun,
|
|
170
|
+
completeFirstRunError,
|
|
156
171
|
};
|
|
157
172
|
}
|
|
@@ -1040,6 +1040,31 @@ function isDefinitiveReplayUploadClientError(status) {
|
|
|
1040
1040
|
return status === 400 || status === 409 || status === 413 || status === 422;
|
|
1041
1041
|
}
|
|
1042
1042
|
const MAX_TRANSIENT_REPLAY_CLIENT_FAILURES = 3;
|
|
1043
|
+
// A hung upload never settles, so `state.flushing` stays set, every later flush
|
|
1044
|
+
// early-returns while still queueing rrweb events, and the queue grows until the
|
|
1045
|
+
// session ends. Time the request out so the failure is observable and the lock
|
|
1046
|
+
// is released.
|
|
1047
|
+
const REPLAY_UPLOAD_TIMEOUT_MS = 15_000;
|
|
1048
|
+
function startReplayUploadTimeout() {
|
|
1049
|
+
// Use an explicit controller/timer pair instead of relying only on
|
|
1050
|
+
// AbortSignal.timeout: the controller is available in older supported
|
|
1051
|
+
// browsers, and the timer can always be cleared when a normal upload
|
|
1052
|
+
// settles so completed uploads do not leave timeout work behind.
|
|
1053
|
+
const controller = typeof AbortController === "undefined" ? undefined : new AbortController();
|
|
1054
|
+
let rejectTimeout;
|
|
1055
|
+
const promise = new Promise((_, reject) => {
|
|
1056
|
+
rejectTimeout = reject;
|
|
1057
|
+
});
|
|
1058
|
+
const timer = setTimeout(() => {
|
|
1059
|
+
controller?.abort();
|
|
1060
|
+
rejectTimeout(new Error("Session replay upload timed out"));
|
|
1061
|
+
}, REPLAY_UPLOAD_TIMEOUT_MS);
|
|
1062
|
+
return {
|
|
1063
|
+
signal: controller?.signal,
|
|
1064
|
+
promise,
|
|
1065
|
+
done: () => clearTimeout(timer),
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1043
1068
|
function isTransientReplayUploadClientError(status) {
|
|
1044
1069
|
return status === 401 || status === 403 || status === 404;
|
|
1045
1070
|
}
|
|
@@ -1048,12 +1073,21 @@ async function sendReplayUpload(options, body, callbacks = {}) {
|
|
|
1048
1073
|
const canUseKeepalive = canUseReplayKeepalive(body);
|
|
1049
1074
|
if (canUseKeepalive)
|
|
1050
1075
|
callbacks.beforeKeepaliveUpload?.();
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1076
|
+
const timeout = startReplayUploadTimeout();
|
|
1077
|
+
let response;
|
|
1078
|
+
try {
|
|
1079
|
+
const request = fetch(options.endpoint, {
|
|
1080
|
+
method: "POST",
|
|
1081
|
+
body,
|
|
1082
|
+
keepalive: canUseKeepalive,
|
|
1083
|
+
headers: { "Content-Type": "text/plain;charset=UTF-8" },
|
|
1084
|
+
signal: timeout.signal,
|
|
1085
|
+
});
|
|
1086
|
+
response = await Promise.race([request, timeout.promise]);
|
|
1087
|
+
}
|
|
1088
|
+
finally {
|
|
1089
|
+
timeout.done();
|
|
1090
|
+
}
|
|
1057
1091
|
if (!response.ok) {
|
|
1058
1092
|
throw new ReplayUploadHttpError(response.status);
|
|
1059
1093
|
}
|
|
@@ -1063,15 +1097,24 @@ async function sendReplayUpload(options, body, callbacks = {}) {
|
|
|
1063
1097
|
const canUseKeepalive = canUseReplayKeepalive(upload.body);
|
|
1064
1098
|
if (canUseKeepalive)
|
|
1065
1099
|
callbacks.beforeKeepaliveUpload?.();
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1100
|
+
const timeout = startReplayUploadTimeout();
|
|
1101
|
+
let response;
|
|
1102
|
+
try {
|
|
1103
|
+
const request = fetch(options.endpoint, {
|
|
1104
|
+
method: "POST",
|
|
1105
|
+
body: upload.body,
|
|
1106
|
+
keepalive: canUseKeepalive,
|
|
1107
|
+
headers: {
|
|
1108
|
+
...upload.headers,
|
|
1109
|
+
"X-Agent-Native-Analytics-Key": options.publicKey,
|
|
1110
|
+
},
|
|
1111
|
+
signal: timeout.signal,
|
|
1112
|
+
});
|
|
1113
|
+
response = await Promise.race([request, timeout.promise]);
|
|
1114
|
+
}
|
|
1115
|
+
finally {
|
|
1116
|
+
timeout.done();
|
|
1117
|
+
}
|
|
1075
1118
|
if (!response.ok) {
|
|
1076
1119
|
throw new ReplayUploadHttpError(response.status);
|
|
1077
1120
|
}
|
|
@@ -3,6 +3,7 @@ import { Tabs, useDesignSystem } from "@agent-native/toolkit/design-system";
|
|
|
3
3
|
import { IconArrowUpRight, IconHistory, IconSearch, IconSettings, IconUserCircle, IconUsers, IconX, } from "@tabler/icons-react";
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
5
5
|
import { Link } from "react-router";
|
|
6
|
+
import { appBasePath, appPath } from "../../client/api-path.js";
|
|
6
7
|
import { buildSettingsRoute } from "../../navigation/index.js";
|
|
7
8
|
import { cn } from "../utils.js";
|
|
8
9
|
function normalizeTabId(value) {
|
|
@@ -70,12 +71,13 @@ function resolveTabId(tabs, value) {
|
|
|
70
71
|
function activeTabFromLocation(tabs, defaultTab) {
|
|
71
72
|
if (typeof window === "undefined")
|
|
72
73
|
return defaultTab;
|
|
74
|
+
const pathname = appLocalPathname();
|
|
73
75
|
const settingsPrefix = "/settings";
|
|
74
|
-
if (
|
|
76
|
+
if (pathname === settingsPrefix) {
|
|
75
77
|
return resolveTabId(tabs, window.location.hash) ?? defaultTab;
|
|
76
78
|
}
|
|
77
|
-
if (
|
|
78
|
-
const segments =
|
|
79
|
+
if (pathname.startsWith(`${settingsPrefix}/`)) {
|
|
80
|
+
const segments = pathname
|
|
79
81
|
.slice(`${settingsPrefix}/`.length)
|
|
80
82
|
.split("/")
|
|
81
83
|
.filter(Boolean)
|
|
@@ -95,6 +97,17 @@ function activeTabFromLocation(tabs, defaultTab) {
|
|
|
95
97
|
}
|
|
96
98
|
return resolveTabId(tabs, window.location.hash) ?? defaultTab;
|
|
97
99
|
}
|
|
100
|
+
function appLocalPathname() {
|
|
101
|
+
if (typeof window === "undefined")
|
|
102
|
+
return "/";
|
|
103
|
+
const pathname = window.location.pathname;
|
|
104
|
+
const basePath = appBasePath();
|
|
105
|
+
if (basePath &&
|
|
106
|
+
(pathname === basePath || pathname.startsWith(`${basePath}/`))) {
|
|
107
|
+
return pathname.slice(basePath.length) || "/";
|
|
108
|
+
}
|
|
109
|
+
return pathname;
|
|
110
|
+
}
|
|
98
111
|
function buildSettingsEntryRoute(tabId, section) {
|
|
99
112
|
const normalizedSection = section?.replace(/^#/, "").trim();
|
|
100
113
|
if (!normalizedSection || normalizedSection === tabId) {
|
|
@@ -112,7 +125,7 @@ function updateRouteForTab(tabId, section) {
|
|
|
112
125
|
if (typeof window === "undefined")
|
|
113
126
|
return;
|
|
114
127
|
const route = buildSettingsEntryRoute(tabId, section);
|
|
115
|
-
window.history.pushState(null, "", `${route}${window.location.search}`);
|
|
128
|
+
window.history.pushState(null, "", `${appPath(route)}${window.location.search}`);
|
|
116
129
|
}
|
|
117
130
|
function isEditableElement(element) {
|
|
118
131
|
if (!(element instanceof HTMLElement))
|
|
@@ -233,7 +246,7 @@ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs =
|
|
|
233
246
|
const fromPath = activeTabFromLocation(tabs, fallbackTab);
|
|
234
247
|
if (fromPath)
|
|
235
248
|
setInternalTab(fromPath);
|
|
236
|
-
if (
|
|
249
|
+
if (appLocalPathname().startsWith("/settings/"))
|
|
237
250
|
return;
|
|
238
251
|
const hashValue = window.location.hash.replace(/^#/, "");
|
|
239
252
|
const fromHash = resolveTabId(tabs, hashValue);
|
|
@@ -254,7 +267,7 @@ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs =
|
|
|
254
267
|
if (!isControlled)
|
|
255
268
|
return;
|
|
256
269
|
const syncControlledLocation = () => {
|
|
257
|
-
const fromPath =
|
|
270
|
+
const fromPath = appLocalPathname().startsWith("/settings/")
|
|
258
271
|
? activeTabFromLocation(tabs, defaultTab)
|
|
259
272
|
: null;
|
|
260
273
|
const hashValue = window.location.hash.replace(/^#/, "");
|
|
@@ -39,10 +39,15 @@ export type ContentPart = {
|
|
|
39
39
|
* call (opt-in `needsApproval` actions). The action did NOT run; the UI
|
|
40
40
|
* renders an Approve/Deny affordance. `approvalKey` is echoed back in
|
|
41
41
|
* `approvedToolCalls` to approve, `dismissed` records a local Deny.
|
|
42
|
+
* `askId` identifies THIS gate hit; it changes when a failed resume
|
|
43
|
+
* re-emits `approval_required` for the same call, which is how the UI
|
|
44
|
+
* tells that apart from the same ask simply re-rendering (see
|
|
45
|
+
* `ApprovalAffordance` in chat/tool-call-display.tsx).
|
|
42
46
|
*/
|
|
43
47
|
approval?: {
|
|
44
48
|
approvalKey: string;
|
|
45
49
|
dismissed?: boolean;
|
|
50
|
+
askId?: string;
|
|
46
51
|
};
|
|
47
52
|
/**
|
|
48
53
|
* Structured metadata from the coding-tools executor side-channel.
|
|
@@ -70,6 +75,8 @@ export interface SSEEvent {
|
|
|
70
75
|
approvalKey?: string;
|
|
71
76
|
/** Model-side tool-call id for `approval_required` (mirrors AgentChatEvent). */
|
|
72
77
|
toolCallId?: string;
|
|
78
|
+
/** Identifies this `approval_required` gate hit (mirrors AgentChatEvent). */
|
|
79
|
+
askId?: string;
|
|
73
80
|
error?: string;
|
|
74
81
|
seq?: number;
|
|
75
82
|
agent?: string;
|
|
@@ -1182,7 +1182,10 @@ export function processEvent(ev, content, toolCallCounter, tabId, state) {
|
|
|
1182
1182
|
if (idx >= 0) {
|
|
1183
1183
|
const part = content[idx];
|
|
1184
1184
|
if (part.type === "tool-call") {
|
|
1185
|
-
part.approval = {
|
|
1185
|
+
part.approval = {
|
|
1186
|
+
approvalKey,
|
|
1187
|
+
...(ev.askId ? { askId: ev.askId } : {}),
|
|
1188
|
+
};
|
|
1186
1189
|
}
|
|
1187
1190
|
}
|
|
1188
1191
|
}
|
|
@@ -12,6 +12,7 @@ export { ErrorBoundary } from "../ErrorBoundary.js";
|
|
|
12
12
|
export { ClientOnly } from "../ClientOnly.js";
|
|
13
13
|
export { DefaultSpinner } from "../DefaultSpinner.js";
|
|
14
14
|
export { RuntimeConfigNotice } from "../RuntimeConfigNotice.js";
|
|
15
|
+
export { EnvironmentBadge, buildEnvironmentUrl, isBuilderIoEmployee, resolveEnvironmentChannel, resolveEnvironmentTargets, type EnvironmentBadgeTargets, } from "../EnvironmentBadge.js";
|
|
15
16
|
export { RouteTransitionIndicator, ROUTE_TRANSITION_INDICATOR_DELAY_MS, } from "../RouteTransitionIndicator.js";
|
|
16
17
|
export { applyEmbeddedThemeUpdate, buildEmbeddedThemeUpdate, EMBEDDED_THEME_CHANGE_EVENT, EMBEDDED_THEME_UPDATE_MESSAGE, getThemeInitScript, parseEmbeddedThemeUpdate, themeInitScript, type EmbeddedThemeUpdate, type NormalizedEmbeddedThemeUpdate, type ResolvedTheme, type ThemePreference, } from "../theme.js";
|
|
17
18
|
export { APPEARANCE_PRESETS, applyAppearance, getStoredAppearance, useAppearance, useAppearanceSync, type AppearancePresetId, } from "../appearance.js";
|
package/dist/client/ui/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { ErrorBoundary } from "../ErrorBoundary.js";
|
|
|
12
12
|
export { ClientOnly } from "../ClientOnly.js";
|
|
13
13
|
export { DefaultSpinner } from "../DefaultSpinner.js";
|
|
14
14
|
export { RuntimeConfigNotice } from "../RuntimeConfigNotice.js";
|
|
15
|
+
export { EnvironmentBadge, buildEnvironmentUrl, isBuilderIoEmployee, resolveEnvironmentChannel, resolveEnvironmentTargets, } from "../EnvironmentBadge.js";
|
|
15
16
|
export { RouteTransitionIndicator, ROUTE_TRANSITION_INDICATOR_DELAY_MS, } from "../RouteTransitionIndicator.js";
|
|
16
17
|
export { applyEmbeddedThemeUpdate, buildEmbeddedThemeUpdate, EMBEDDED_THEME_CHANGE_EVENT, EMBEDDED_THEME_UPDATE_MESSAGE, getThemeInitScript, parseEmbeddedThemeUpdate, themeInitScript, } from "../theme.js";
|
|
17
18
|
export { APPEARANCE_PRESETS, applyAppearance, getStoredAppearance, useAppearance, useAppearanceSync, } from "../appearance.js";
|
|
@@ -52,6 +52,11 @@ function isActionTimeout(error) {
|
|
|
52
52
|
typeof error === "object" &&
|
|
53
53
|
error.timedOut === true);
|
|
54
54
|
}
|
|
55
|
+
function isActionMethodMismatch(error) {
|
|
56
|
+
return (!!error &&
|
|
57
|
+
typeof error === "object" &&
|
|
58
|
+
error.code === "action_method_mismatch");
|
|
59
|
+
}
|
|
55
60
|
/** @internal exported for tests */
|
|
56
61
|
export function defaultActionQueryRetry(failureCount, error) {
|
|
57
62
|
if (isAuthFailure(error))
|
|
@@ -60,6 +65,9 @@ export function defaultActionQueryRetry(failureCount, error) {
|
|
|
60
65
|
// silently retrying would multiply that wait. Surface it instead.
|
|
61
66
|
if (isActionTimeout(error))
|
|
62
67
|
return false;
|
|
68
|
+
// Wrong verb is deterministic — retrying sends the same wrong verb again.
|
|
69
|
+
if (isActionMethodMismatch(error))
|
|
70
|
+
return false;
|
|
63
71
|
if (isBrowserResourceExhaustion(error))
|
|
64
72
|
return false;
|
|
65
73
|
// Network-level failures never carry an HTTP `status` (actionFetch only
|
|
@@ -338,6 +346,24 @@ async function performActionFetch(name, method, params, options) {
|
|
|
338
346
|
(raw && raw.slice(0, 200)) ||
|
|
339
347
|
res.statusText ||
|
|
340
348
|
`HTTP ${res.status}`;
|
|
349
|
+
// mountActionRoutes only ever returns 405 for one reason: the verb this
|
|
350
|
+
// call sent doesn't match the action's declared `http.method`. The client
|
|
351
|
+
// has no way to look that method up ahead of time (defineAction lives in
|
|
352
|
+
// server-only modules), so callers routinely default to POST and hit this
|
|
353
|
+
// blind. Name the action and the required method instead of leaving a
|
|
354
|
+
// bare 405 for the caller to reverse-engineer from a "Use X" string.
|
|
355
|
+
if (res.status === 405) {
|
|
356
|
+
const requiredMethod = /\bUse (GET|POST|PUT|DELETE)\b/.exec(message)?.[1];
|
|
357
|
+
const error = new Error(`Action ${name} was called with ${method}, but it declares ` +
|
|
358
|
+
`http: { method: "${requiredMethod ?? "?"}" }. Pass { method: "${requiredMethod ?? "..."}" } ` +
|
|
359
|
+
`to this call (or use the hook that defaults to it) to match the action's declared method.`);
|
|
360
|
+
error.status = 405;
|
|
361
|
+
error.code = "action_method_mismatch";
|
|
362
|
+
error.sentMethod = method;
|
|
363
|
+
if (requiredMethod)
|
|
364
|
+
error.requiredMethod = requiredMethod;
|
|
365
|
+
throw error;
|
|
366
|
+
}
|
|
341
367
|
const error = new Error(`Action ${name} failed: ${message}`);
|
|
342
368
|
error.status = res.status;
|
|
343
369
|
throw error;
|
|
@@ -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;
|
|
65
66
|
states: {
|
|
66
67
|
clientId: number;
|
|
67
68
|
state: string;
|
|
68
69
|
}[];
|
|
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;
|
|
80
81
|
users: {
|
|
81
82
|
clientId: number;
|
|
82
83
|
lastSeen: number;
|
|
83
84
|
}[];
|
|
84
|
-
error?: undefined;
|
|
85
85
|
}>>;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
ok?: undefined;
|
|
30
29
|
error: string;
|
|
30
|
+
ok?: undefined;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
package/dist/config.d.ts
CHANGED
|
@@ -38,6 +38,11 @@ export interface AgentNativeRuntimeConfig {
|
|
|
38
38
|
database?: AgentNativeRuntimeDatabaseConfig;
|
|
39
39
|
environment?: AgentNativeRuntimeEnvironmentConfig;
|
|
40
40
|
}
|
|
41
|
+
export type AgentNativeDeploymentEnvironment = "local" | "beta" | "production" | "preview";
|
|
42
|
+
export interface AgentNativeDeploymentConfig {
|
|
43
|
+
/** The release lane that produced the currently running client bundle. */
|
|
44
|
+
environment?: AgentNativeDeploymentEnvironment;
|
|
45
|
+
}
|
|
41
46
|
export interface AgentNativeDiagnosticsConfig {
|
|
42
47
|
/** Fail a production Vite build when runtime configuration has issues. */
|
|
43
48
|
failOnBuild?: boolean;
|
|
@@ -73,6 +78,7 @@ export interface AgentNativeConfig {
|
|
|
73
78
|
version?: typeof AGENT_NATIVE_CONFIG_VERSION;
|
|
74
79
|
onboarding?: AgentNativeOnboardingConfig;
|
|
75
80
|
runtime?: AgentNativeRuntimeConfig;
|
|
81
|
+
deployment?: AgentNativeDeploymentConfig;
|
|
76
82
|
diagnostics?: AgentNativeDiagnosticsConfig;
|
|
77
83
|
instructions?: AgentNativeInstructionsConfig;
|
|
78
84
|
translations?: AgentNativeTranslationsConfig;
|
|
@@ -99,3 +105,12 @@ export declare function normalizeAgentNativeConfig(input: unknown, source?: stri
|
|
|
99
105
|
export declare function mergeAgentNativeConfigs(base: AgentNativeConfig, override: AgentNativeConfig): AgentNativeConfig;
|
|
100
106
|
export declare function resolveAgentNativeConfig(input: AgentNativeConfigInput | undefined, context: AgentNativeConfigContext): AgentNativeConfig;
|
|
101
107
|
export declare function resolveFirstRunOnboardingMode(setting: AgentNativeFirstRunOnboardingSetting, context: AgentNativeConfigContext): AgentNativeFirstRunOnboardingMode;
|
|
108
|
+
export declare function isAgentNativeDeploymentEnvironment(value: unknown): value is AgentNativeDeploymentEnvironment;
|
|
109
|
+
/**
|
|
110
|
+
* Resolve the public deployment lane from hosting facts at build time.
|
|
111
|
+
*
|
|
112
|
+
* Netlify exposes `CONTEXT` and `BRANCH` to builds. Keeping this inference in
|
|
113
|
+
* the Vite/config boundary means browser code consumes typed public config and
|
|
114
|
+
* never parses process.env itself.
|
|
115
|
+
*/
|
|
116
|
+
export declare function inferAgentNativeDeploymentEnvironment(env: Record<string, string | undefined>, mode?: string): AgentNativeDeploymentEnvironment | undefined;
|
package/dist/config.js
CHANGED
|
@@ -26,6 +26,7 @@ export function normalizeAgentNativeConfig(input, source = "agent-native config"
|
|
|
26
26
|
}
|
|
27
27
|
const onboardingValue = input.onboarding;
|
|
28
28
|
const runtimeValue = input.runtime;
|
|
29
|
+
const deploymentValue = input.deployment;
|
|
29
30
|
const diagnosticsValue = input.diagnostics;
|
|
30
31
|
const instructionsValue = input.instructions;
|
|
31
32
|
const translationsValue = input.translations;
|
|
@@ -46,6 +47,9 @@ export function normalizeAgentNativeConfig(input, source = "agent-native config"
|
|
|
46
47
|
if (runtimeValue !== undefined) {
|
|
47
48
|
normalized.runtime = normalizeRuntimeConfig(runtimeValue, `${source}.runtime`);
|
|
48
49
|
}
|
|
50
|
+
if (deploymentValue !== undefined) {
|
|
51
|
+
normalized.deployment = normalizeDeploymentConfig(deploymentValue, `${source}.deployment`);
|
|
52
|
+
}
|
|
49
53
|
if (diagnosticsValue !== undefined) {
|
|
50
54
|
normalized.diagnostics = normalizeDiagnosticsConfig(diagnosticsValue, `${source}.diagnostics`);
|
|
51
55
|
}
|
|
@@ -101,6 +105,12 @@ export function mergeAgentNativeConfigs(base, override) {
|
|
|
101
105
|
: undefined,
|
|
102
106
|
}
|
|
103
107
|
: undefined,
|
|
108
|
+
deployment: base.deployment || override.deployment
|
|
109
|
+
? {
|
|
110
|
+
...base.deployment,
|
|
111
|
+
...override.deployment,
|
|
112
|
+
}
|
|
113
|
+
: undefined,
|
|
104
114
|
diagnostics: base.diagnostics || override.diagnostics
|
|
105
115
|
? {
|
|
106
116
|
...base.diagnostics,
|
|
@@ -205,6 +215,18 @@ function normalizeRuntimeConfig(value, source) {
|
|
|
205
215
|
}
|
|
206
216
|
return result;
|
|
207
217
|
}
|
|
218
|
+
function normalizeDeploymentConfig(value, source) {
|
|
219
|
+
if (!isRecord(value)) {
|
|
220
|
+
throw new Error(`${source} must be an object`);
|
|
221
|
+
}
|
|
222
|
+
const environment = value.environment;
|
|
223
|
+
if (environment === undefined)
|
|
224
|
+
return {};
|
|
225
|
+
if (!isAgentNativeDeploymentEnvironment(environment)) {
|
|
226
|
+
throw new Error(`${source}.environment must be "local", "beta", "production", or "preview"`);
|
|
227
|
+
}
|
|
228
|
+
return { environment };
|
|
229
|
+
}
|
|
208
230
|
function normalizeDiagnosticsConfig(value, source) {
|
|
209
231
|
if (!isRecord(value)) {
|
|
210
232
|
throw new Error(`${source} must be an object`);
|
|
@@ -338,6 +360,37 @@ function isAgentNativeHarnessRuntime(value) {
|
|
|
338
360
|
value === "pi" ||
|
|
339
361
|
value === "opencode");
|
|
340
362
|
}
|
|
363
|
+
export function isAgentNativeDeploymentEnvironment(value) {
|
|
364
|
+
return (value === "local" ||
|
|
365
|
+
value === "beta" ||
|
|
366
|
+
value === "production" ||
|
|
367
|
+
value === "preview");
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Resolve the public deployment lane from hosting facts at build time.
|
|
371
|
+
*
|
|
372
|
+
* Netlify exposes `CONTEXT` and `BRANCH` to builds. Keeping this inference in
|
|
373
|
+
* the Vite/config boundary means browser code consumes typed public config and
|
|
374
|
+
* never parses process.env itself.
|
|
375
|
+
*/
|
|
376
|
+
export function inferAgentNativeDeploymentEnvironment(env, mode) {
|
|
377
|
+
const context = env.CONTEXT?.trim().toLowerCase();
|
|
378
|
+
const branch = env.BRANCH?.trim().toLowerCase();
|
|
379
|
+
if (branch === "production" || context === "production") {
|
|
380
|
+
return "production";
|
|
381
|
+
}
|
|
382
|
+
if (branch === "beta" || (context === "branch-deploy" && branch === "main")) {
|
|
383
|
+
return "beta";
|
|
384
|
+
}
|
|
385
|
+
if (context === "deploy-preview" ||
|
|
386
|
+
context === "branch-deploy" ||
|
|
387
|
+
branch?.startsWith("deploy-preview")) {
|
|
388
|
+
return "preview";
|
|
389
|
+
}
|
|
390
|
+
if (mode === "development")
|
|
391
|
+
return "local";
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
341
394
|
function isRecord(value) {
|
|
342
395
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
343
396
|
}
|